From 5140c099b3985877d37b2cdcfc4d7711c40c6aeb Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 31 Jul 2013 22:57:11 -0500 Subject: remove --enable-localinstall --- src/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 6bbb623..8f8d7eb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,5 @@ pkglibexec_PROGRAMS = indicator-bluetooth-service -indicatordir = $(INDICATORDIR) +indicatordir = ${libdir}/indicators3/7 indicator_LTLIBRARIES = libbluetooth.la indicator_bluetooth_service_SOURCES = \ -- cgit v1.2.3 From 761a9fff03812af3c0beb26da1094c69fc3a0a72 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 31 Jul 2013 23:04:40 -0500 Subject: add a vapi/ directory off the top package directory --- src/Makefile.am | 9 +++++++-- src/config.vapi | 3 --- src/indicator-bluetooth-service.vala | 6 +++--- 3 files changed, 10 insertions(+), 8 deletions(-) delete mode 100644 src/config.vapi (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 8f8d7eb..5fcfc5c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,12 +3,15 @@ indicatordir = ${libdir}/indicators3/7 indicator_LTLIBRARIES = libbluetooth.la indicator_bluetooth_service_SOURCES = \ - config.vapi \ indicator3-0.4.vapi \ gnome-bluetooth-1.0.vapi \ indicator-bluetooth-service.vala indicator_bluetooth_service_VALAFLAGS = \ + --ccode \ + --vapidir=$(top_srcdir)/vapi/ \ + --vapidir=./ \ + --pkg config \ --pkg posix \ --pkg glib-2.0 \ --pkg gtk+-3.0 \ @@ -23,12 +26,14 @@ indicator_bluetooth_service_LDADD = \ $(INDICATOR_BLUETOOTH_SERVICE_LIBS) libbluetooth_la_SOURCES = \ - config.vapi \ indicator3-0.4.vapi \ indicator-bluetooth.vala \ libido3-0.1.vapi libbluetooth_la_VALAFLAGS = \ + --ccode \ + --vapidir=$(top_srcdir)/vapi/ \ + --vapidir=./ \ --pkg posix \ --pkg glib-2.0 \ --pkg gtk+-3.0 \ diff --git a/src/config.vapi b/src/config.vapi deleted file mode 100644 index a1bb094..0000000 --- a/src/config.vapi +++ /dev/null @@ -1,3 +0,0 @@ -public const string VERSION; -public const string GETTEXT_PACKAGE; -public const string LOCALE_DIR; diff --git a/src/indicator-bluetooth-service.vala b/src/indicator-bluetooth-service.vala index ac84f33..e9d8e76 100644 --- a/src/indicator-bluetooth-service.vala +++ b/src/indicator-bluetooth-service.vala @@ -397,9 +397,9 @@ private void show_control_center (string panel) public static int main (string[] args) { Intl.setlocale (LocaleCategory.ALL, ""); - Intl.bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR); - Intl.bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); - Intl.textdomain (GETTEXT_PACKAGE); + Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.GNOMELOCALEDIR); + Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8"); + Intl.textdomain (Config.GETTEXT_PACKAGE); var loop = new MainLoop (); -- cgit v1.2.3 From 39263df5ab76e2f444e5dbb702512f741c56830c Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 31 Jul 2013 23:09:41 -0500 Subject: add main() to its own file --- src/Makefile.am | 1 + src/indicator-bluetooth-service.vala | 28 ---------------------------- src/main.vala | 27 +++++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 28 deletions(-) create mode 100644 src/main.vala (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 5fcfc5c..2b92f9b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,6 +5,7 @@ indicator_LTLIBRARIES = libbluetooth.la indicator_bluetooth_service_SOURCES = \ indicator3-0.4.vapi \ gnome-bluetooth-1.0.vapi \ + main.vala \ indicator-bluetooth-service.vala indicator_bluetooth_service_VALAFLAGS = \ diff --git a/src/indicator-bluetooth-service.vala b/src/indicator-bluetooth-service.vala index e9d8e76..f65c538 100644 --- a/src/indicator-bluetooth-service.vala +++ b/src/indicator-bluetooth-service.vala @@ -394,34 +394,6 @@ private void show_control_center (string panel) } } -public static int main (string[] args) -{ - Intl.setlocale (LocaleCategory.ALL, ""); - Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.GNOMELOCALEDIR); - Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8"); - Intl.textdomain (Config.GETTEXT_PACKAGE); - - var loop = new MainLoop (); - - BluetoothIndicator indicator; - try - { - indicator = new BluetoothIndicator (); - } - catch (Error e) - { - warning ("Failed to start bluetooth indicator service: %s", e.message); - return Posix.EXIT_FAILURE; - } - // FIXMEindicator.shutdown.connect (() => { loop.quit (); }); - - loop.run (); - - indicator = null; - - return Posix.EXIT_SUCCESS; -} - [DBus (name = "com.canonical.indicator.bluetooth.service")] private class BluetoothService : Object { diff --git a/src/main.vala b/src/main.vala new file mode 100644 index 0000000..79683b2 --- /dev/null +++ b/src/main.vala @@ -0,0 +1,27 @@ + +public static int +main (string[] args) +{ + Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8"); + Intl.setlocale (LocaleCategory.ALL, ""); + Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.GNOMELOCALEDIR); + Intl.textdomain (Config.GETTEXT_PACKAGE); + + var loop = new MainLoop (); + + BluetoothIndicator indicator; + try + { + indicator = new BluetoothIndicator (); + } + catch (Error e) + { + warning ("Failed to start bluetooth indicator service: %s", e.message); + return Posix.EXIT_FAILURE; + } + + loop.run (); + return Posix.EXIT_SUCCESS; +} + //var service = new IndicatorSound.Service (); + //return service.run (); -- cgit v1.2.3 From 3cc732f558737acd0004efb013993515845e67d7 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 1 Aug 2013 01:34:47 -0500 Subject: add per-profile icons and initial menus w/settings section --- src/Makefile.am | 34 +-- src/desktop.vala | 99 ++++++++ src/gnome-bluetooth-1.0.vapi | 82 ------ src/indicator-bluetooth-service.vala | 415 ------------------------------- src/indicator-bluetooth.vala | 142 ----------- src/main.vala | 17 +- src/menu.vala | 53 ++++ src/phone.vala | 76 ++++++ src/service.vala | 470 +++++++++++++++++++++++++++++++++++ 9 files changed, 705 insertions(+), 683 deletions(-) create mode 100644 src/desktop.vala delete mode 100644 src/gnome-bluetooth-1.0.vapi delete mode 100644 src/indicator-bluetooth-service.vala delete mode 100644 src/indicator-bluetooth.vala create mode 100644 src/menu.vala create mode 100644 src/phone.vala create mode 100644 src/service.vala (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 2b92f9b..ed2c976 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,17 +1,18 @@ pkglibexec_PROGRAMS = indicator-bluetooth-service -indicatordir = ${libdir}/indicators3/7 -indicator_LTLIBRARIES = libbluetooth.la indicator_bluetooth_service_SOURCES = \ indicator3-0.4.vapi \ - gnome-bluetooth-1.0.vapi \ + desktop.vala \ main.vala \ - indicator-bluetooth-service.vala + menu.vala \ + phone.vala \ + service.vala indicator_bluetooth_service_VALAFLAGS = \ --ccode \ --vapidir=$(top_srcdir)/vapi/ \ --vapidir=./ \ + --pkg gnome-bluetooth-1.0 \ --pkg config \ --pkg posix \ --pkg glib-2.0 \ @@ -26,31 +27,6 @@ indicator_bluetooth_service_CFLAGS = \ indicator_bluetooth_service_LDADD = \ $(INDICATOR_BLUETOOTH_SERVICE_LIBS) -libbluetooth_la_SOURCES = \ - indicator3-0.4.vapi \ - indicator-bluetooth.vala \ - libido3-0.1.vapi - -libbluetooth_la_VALAFLAGS = \ - --ccode \ - --vapidir=$(top_srcdir)/vapi/ \ - --vapidir=./ \ - --pkg posix \ - --pkg glib-2.0 \ - --pkg gtk+-3.0 \ - --pkg Dbusmenu-0.4 \ - --pkg DbusmenuGtk3-0.4 - -libbluetooth_la_CFLAGS = \ - -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \ - $(INDICATOR_BLUETOOTH_CFLAGS) - -libbluetooth_la_LIBADD = \ - $(INDICATOR_BLUETOOTH_LIBS) - -libbluetooth_la_LDFLAGS = \ - -module -avoid-version - CLEANFILES = \ $(patsubst %.vala,%.c,$(filter %.vala, $(SOURCES))) \ *_vala.stamp diff --git a/src/desktop.vala b/src/desktop.vala new file mode 100644 index 0000000..bd005f5 --- /dev/null +++ b/src/desktop.vala @@ -0,0 +1,99 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +class DesktopMenu: BluetoothMenu +{ + private Settings settings; + + private Action[] actions; + + public override void add_actions_to_group (SimpleActionGroup group) + { + base.add_actions_to_group (group); + + for (var i=0; i { message("visible toggled"); }); + + this.actions = {}; + this.actions += new SimpleAction.stateful ("root-desktop", null, action_state_for_root()); + this.actions += create_settings_action (); + this.actions += create_wizard_action (); + + var section = new Menu (); + section.append (_("Set Up New Device…"), "indicator.desktop-wizard"); + section.append (_("Bluetooth Settings…"), "indicator.desktop-settings"); + this.menu.append_section (null, section); + } + + Action create_wizard_action () + { + var action = new SimpleAction ("desktop-wizard", null); + + action.activate.connect ((action, param) => { + try { + Process.spawn_command_line_async ("bluetooth-wizard"); + } catch (Error e) { + warning ("unable to launch settings: %s", e.message); + } + }); + + return action; + } + + Action create_settings_action () + { + var action = new SimpleAction ("desktop-settings", null); + + action.activate.connect ((action, param) => { + try { + Process.spawn_command_line_async ("gnome-control-center bluetooth"); + } catch (Error e) { + warning ("unable to launch settings: %s", e.message); + } + }); + + return action; + } + + protected Variant action_state_for_root () + { + var label = "Hello"; // FIXME + var a11y = "Hello"; // FIXME + var visible = true; // FIXME + + string icon_name = "bluetooth-active"; // FIXME: enabled, disabled, connected, etc. +//indicator-bluetooth-service.vala: bluetooth_service._icon_name = enabled ? "bluetooth-active" : "bluetooth-disabled"; + var icon = new ThemedIcon.with_default_fallbacks (icon_name); + + var builder = new VariantBuilder (new VariantType ("a{sv}")); + builder.add ("{sv}", "visible", new Variant ("b", visible)); + builder.add ("{sv}", "label", new Variant ("s", label)); + builder.add ("{sv}", "accessible-desc", new Variant ("s", a11y)); + builder.add ("{sv}", "icon", icon.serialize()); + return builder.end (); + } +} diff --git a/src/gnome-bluetooth-1.0.vapi b/src/gnome-bluetooth-1.0.vapi deleted file mode 100644 index 6d6aa09..0000000 --- a/src/gnome-bluetooth-1.0.vapi +++ /dev/null @@ -1,82 +0,0 @@ -[CCode (cprefix = "Bluetooth", lower_case_cprefix = "bluetooth_")] -namespace GnomeBluetooth -{ - -[CCode (cheader_filename = "bluetooth-client.h")] -public class Client : GLib.Object -{ - public Client (); - public Gtk.TreeModel model { get; } - public Gtk.TreeModel adapter_model { get; } - public Gtk.TreeModel device_model { get; } - [CCode (finish_function = "bluetooth_client_connect_service_finish")] - public async bool connect_service (string device, bool connect, GLib.Cancellable? cancellable = null) throws GLib.Error; -} - -[CCode (cheader_filename = "bluetooth-enums.h", cprefix = "BLUETOOTH_COLUMN_")] -public enum Column -{ - PROXY, - ADDRESS, - ALIAS, - NAME, - TYPE, - ICON, - DEFAULT, - PAIRED, - TRUSTED, - CONNECTED, - DISCOVERABLE, - DISCOVERING, - LEGACYPAIRING, - POWERED, - SERVICES, - UUIDS -} - -[CCode (cheader_filename = "bluetooth-enums.h", cprefix = "BLUETOOTH_TYPE_")] -public enum Type -{ - ANY, - PHONE, - MODEM, - COMPUTER, - NETWORK, - HEADSET, - HEADPHONES, - OTHER_AUDIO, - KEYBOARD, - MOUSE, - CAMERA, - PRINTER, - JOYPAD, - TABLET, - VIDEO -} - -[CCode (cheader_filename = "bluetooth-utils.h")] -public void browse_address (GLib.Object? object, string address, uint timestamp, GLib.AsyncReadyCallback? callback); - -[CCode (cheader_filename = "bluetooth-utils.h")] -public void send_to_address (string address, string alias); - -[CCode (cheader_filename = "bluetooth-killswitch.h", cprefix = "BLUETOOTH_KILLSWITCH_STATE_")] -public enum KillswitchState -{ - NO_ADAPTER, - SOFT_BLOCKED, - UNBLOCKED, - HARD_BLOCKED -} - -[CCode (cheader_filename = "bluetooth-killswitch.h")] -public class Killswitch : GLib.Object -{ - public Killswitch (); - public signal void state_changed (KillswitchState state); - public bool has_killswitches (); - public KillswitchState state { get; set; } - public unowned string state_to_string (); -} - -} diff --git a/src/indicator-bluetooth-service.vala b/src/indicator-bluetooth-service.vala deleted file mode 100644 index f65c538..0000000 --- a/src/indicator-bluetooth-service.vala +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Copyright (C) 2012-2013 Canonical Ltd. - * Author: Robert Ancell - * - * 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 of the License. - * See http://www.gnu.org/copyleft/gpl.html the full text of the license. - */ - -public class BluetoothIndicator -{ - private Settings settings; - private DBusConnection bus; - private Indicator.Service indicator_service; - private Dbusmenu.Server menu_server; - private BluetoothService bluetooth_service; - private GnomeBluetooth.Client client; - private GnomeBluetooth.Killswitch killswitch; - private bool updating_killswitch = false; - private Dbusmenu.Menuitem enable_item; - private Dbusmenu.Menuitem visible_item; - private bool updating_visible = false; - private Dbusmenu.Menuitem devices_separator; - private List device_items; - private Dbusmenu.Menuitem menu; - - public BluetoothIndicator () throws Error - { - settings = new Settings ("com.canonical.indicator.bluetooth"); - settings.changed.connect ((key) => - { - if (key == "visible") - update_visible (); - }); - - bus = Bus.get_sync (BusType.SESSION); - - indicator_service = new Indicator.Service ("com.canonical.indicator.bluetooth"); - menu_server = new Dbusmenu.Server ("/com/canonical/indicator/bluetooth/menu"); - - bluetooth_service = new BluetoothService (); - bus.register_object ("/com/canonical/indicator/bluetooth/service", bluetooth_service); - - killswitch = new GnomeBluetooth.Killswitch (); - killswitch.state_changed.connect (killswitch_state_changed_cb); - - client = new GnomeBluetooth.Client (); - - menu = new Dbusmenu.Menuitem (); - menu_server.set_root (menu); - - enable_item = new Dbusmenu.Menuitem (); - enable_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Bluetooth")); - enable_item.property_set (Dbusmenu.MENUITEM_PROP_TYPE, "x-canonical-switch"); - enable_item.item_activated.connect (() => - { - if (updating_killswitch) - return; - if (killswitch.state == GnomeBluetooth.KillswitchState.UNBLOCKED) - killswitch.state = GnomeBluetooth.KillswitchState.SOFT_BLOCKED; - else - killswitch.state = GnomeBluetooth.KillswitchState.UNBLOCKED; - }); - menu.child_append (enable_item); - - visible_item = new Dbusmenu.Menuitem (); - visible_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Visible")); - visible_item.property_set (Dbusmenu.MENUITEM_PROP_TYPE, "x-canonical-switch"); - bool discoverable; - client.get ("default-adapter-discoverable", out discoverable); - visible_item.property_set_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE, discoverable ? Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED : Dbusmenu.MENUITEM_TOGGLE_STATE_UNCHECKED); - client.notify["default-adapter-discoverable"].connect (() => - { - updating_visible = true; - bool is_discoverable; - client.get ("default-adapter-discoverable", out is_discoverable); - visible_item.property_set_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE, is_discoverable ? Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED : Dbusmenu.MENUITEM_TOGGLE_STATE_UNCHECKED); - updating_visible = false; - }); - visible_item.item_activated.connect (() => - { - if (updating_visible) - return; - client.set ("default-adapter-discoverable", visible_item.property_get_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE) != Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED); - }); - menu.child_append (visible_item); - - devices_separator = new Dbusmenu.Menuitem (); - devices_separator.property_set (Dbusmenu.MENUITEM_PROP_TYPE, Dbusmenu.CLIENT_TYPES_SEPARATOR); - menu.child_append (devices_separator); - - device_items = new List (); - - client.model.row_inserted.connect (device_changed_cb); - client.model.row_changed.connect (device_changed_cb); - client.model.row_deleted.connect (device_removed_cb); - Gtk.TreeIter iter; - var have_iter = client.model.get_iter_first (out iter); - while (have_iter) - { - Gtk.TreeIter child_iter; - var have_child_iter = client.model.iter_children (out child_iter, iter); - while (have_child_iter) - { - device_changed_cb (null, child_iter); - have_child_iter = client.model.iter_next (ref child_iter); - } - have_iter = client.model.iter_next (ref iter); - } - - var sep = new Dbusmenu.Menuitem (); - sep.property_set (Dbusmenu.MENUITEM_PROP_TYPE, Dbusmenu.CLIENT_TYPES_SEPARATOR); - menu.child_append (sep); - - var item = new Dbusmenu.Menuitem (); - item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Set Up New Device…")); - item.item_activated.connect (() => { set_up_new_device (); }); - menu.child_append (item); - - item = new Dbusmenu.Menuitem (); - item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Bluetooth Settings…")); - item.item_activated.connect (() => { show_control_center ("bluetooth"); }); - menu.child_append (item); - - killswitch_state_changed_cb (killswitch.state); - - client.adapter_model.row_inserted.connect (update_visible); - client.adapter_model.row_deleted.connect (update_visible); - update_visible (); - } - - private BluetoothMenuItem? find_menu_item (string address) - { - foreach (var item in device_items) - if (item.address == address) - return item; - - return null; - } - - private void device_changed_cb (Gtk.TreePath? path, Gtk.TreeIter iter) - { - /* Ignore adapters */ - Gtk.TreeIter parent_iter; - if (!client.model.iter_parent (out parent_iter, iter)) - return; - - DBusProxy proxy; - string address; - string alias; - GnomeBluetooth.Type type; - string icon; - bool connected; - HashTable services; - string[] uuids; - client.model.get (iter, - GnomeBluetooth.Column.PROXY, out proxy, - GnomeBluetooth.Column.ADDRESS, out address, - GnomeBluetooth.Column.ALIAS, out alias, - GnomeBluetooth.Column.TYPE, out type, - GnomeBluetooth.Column.ICON, out icon, - GnomeBluetooth.Column.CONNECTED, out connected, - GnomeBluetooth.Column.SERVICES, out services, - GnomeBluetooth.Column.UUIDS, out uuids); - - /* Skip if haven't actually got any information yet */ - if (proxy == null) - return; - - /* Find or create menu item */ - var item = find_menu_item (address); - if (item == null) - { - item = new BluetoothMenuItem (client, address); - item.property_set_bool (Dbusmenu.MENUITEM_PROP_VISIBLE, killswitch.state == GnomeBluetooth.KillswitchState.UNBLOCKED); - var last_item = devices_separator as Dbusmenu.Menuitem; - if (device_items != null) - last_item = device_items.last ().data; - device_items.append (item); - menu.child_add_position (item, last_item.get_position (menu) + 1); - } - - item.update (type, proxy, alias, icon, connected, services, uuids); - } - - private void update_visible () - { - bluetooth_service._visible = client.adapter_model.iter_n_children (null) > 0 && settings.get_boolean ("visible"); - var builder = new VariantBuilder (VariantType.ARRAY); - builder.add ("{sv}", "Visible", new Variant.boolean (bluetooth_service._visible)); - try - { - var properties = new Variant ("(sa{sv}as)", "com.canonical.indicator.bluetooth.service", builder, null); - bus.emit_signal (null, - "/com/canonical/indicator/bluetooth/service", - "org.freedesktop.DBus.Properties", - "PropertiesChanged", - properties); - } - catch (Error e) - { - warning ("Failed to emit signal: %s", e.message); - } - } - - private void device_removed_cb (Gtk.TreePath path) - { - Gtk.TreeIter iter; - if (!client.model.get_iter (out iter, path)) - return; - - string address; - client.model.get (iter, GnomeBluetooth.Column.ADDRESS, out address); - - var item = find_menu_item (address); - if (item == null) - return; - - device_items.remove (item); - menu.child_delete (item); - } - - private void killswitch_state_changed_cb (GnomeBluetooth.KillswitchState state) - { - updating_killswitch = true; - - var enabled = state == GnomeBluetooth.KillswitchState.UNBLOCKED; - - bluetooth_service._icon_name = enabled ? "bluetooth-active" : "bluetooth-disabled"; - bluetooth_service._accessible_description = enabled ? _("Bluetooth: On") : _("Bluetooth: Off"); - - var builder = new VariantBuilder (VariantType.ARRAY); - builder.add ("{sv}", "IconName", new Variant.string (bluetooth_service._icon_name)); - builder.add ("{sv}", "AccessibleDescription", new Variant.string (bluetooth_service._accessible_description)); - try - { - var properties = new Variant ("(sa{sv}as)", "com.canonical.indicator.bluetooth.service", builder, null); - bus.emit_signal (null, - "/com/canonical/indicator/bluetooth/service", - "org.freedesktop.DBus.Properties", - "PropertiesChanged", - properties); - } - catch (Error e) - { - warning ("Failed to emit signal: %s", e.message); - } - - enable_item.property_set_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE, enabled ? Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED : Dbusmenu.MENUITEM_TOGGLE_STATE_UNCHECKED); - - /* Disable devices when locked */ - visible_item.property_set_bool (Dbusmenu.MENUITEM_PROP_VISIBLE, enabled); - devices_separator.property_set_bool (Dbusmenu.MENUITEM_PROP_VISIBLE, enabled); - foreach (var item in device_items) - item.property_set_bool (Dbusmenu.MENUITEM_PROP_VISIBLE, enabled && item.get_children () != null); - - updating_killswitch = false; - } -} - -private class BluetoothMenuItem : Dbusmenu.Menuitem -{ - private GnomeBluetooth.Client client; - public string address; - private Dbusmenu.Menuitem? connect_item = null; - private bool make_submenu = false; - - public BluetoothMenuItem (GnomeBluetooth.Client client, string address) - { - this.client = client; - this.address = address; - } - - public void update (GnomeBluetooth.Type type, DBusProxy proxy, string alias, string icon, bool connected, HashTable? services, string[] uuids) - { - property_set (Dbusmenu.MENUITEM_PROP_LABEL, alias); - property_set (Dbusmenu.MENUITEM_PROP_ICON_NAME, icon); - if (connect_item != null) - connect_item.property_set_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE, connected ? Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED : Dbusmenu.MENUITEM_TOGGLE_STATE_UNCHECKED); - - /* FIXME: Not sure if the GUI elements below can change over time */ - if (make_submenu) - return; - make_submenu = true; - - if (services != null) - { - connect_item = new Dbusmenu.Menuitem (); - connect_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Connection")); - connect_item.property_set (Dbusmenu.MENUITEM_PROP_TYPE, "x-canonical-switch"); - connect_item.property_set_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE, connected ? Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED : Dbusmenu.MENUITEM_TOGGLE_STATE_UNCHECKED); - connect_item.item_activated.connect (() => { connect_service (proxy.get_object_path (), connect_item.property_get_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE) != Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED); }); - child_append (connect_item); - } - - var can_send = false; - var can_browse = false; - if (uuids != null) - { - for (var i = 0; uuids[i] != null; i++) - { - if (uuids[i] == "OBEXObjectPush") - can_send = true; - if (uuids[i] == "OBEXFileTransfer") - can_browse = true; - } - } - - if (can_send) - { - var send_item = new Dbusmenu.Menuitem (); - send_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Send files…")); - send_item.item_activated.connect (() => { GnomeBluetooth.send_to_address (address, alias); }); - child_append (send_item); - } - - if (can_browse) - { - var browse_item = new Dbusmenu.Menuitem (); - browse_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Browse files…")); - browse_item.item_activated.connect (() => { GnomeBluetooth.browse_address (null, address, Gdk.CURRENT_TIME, null); }); - child_append (browse_item); - } - - switch (type) - { - case GnomeBluetooth.Type.KEYBOARD: - var keyboard_item = new Dbusmenu.Menuitem (); - keyboard_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Keyboard Settings…")); - keyboard_item.item_activated.connect (() => { show_control_center ("keyboard"); }); - child_append (keyboard_item); - break; - - case GnomeBluetooth.Type.MOUSE: - case GnomeBluetooth.Type.TABLET: - var mouse_item = new Dbusmenu.Menuitem (); - mouse_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Mouse and Touchpad Settings…")); - mouse_item.item_activated.connect (() => { show_control_center ("mouse"); }); - child_append (mouse_item); - break; - - case GnomeBluetooth.Type.HEADSET: - case GnomeBluetooth.Type.HEADPHONES: - case GnomeBluetooth.Type.OTHER_AUDIO: - var sound_item = new Dbusmenu.Menuitem (); - sound_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Sound Settings…")); - sound_item.item_activated.connect (() => { show_control_center ("sound"); }); - child_append (sound_item); - break; - } - - property_set_bool (Dbusmenu.MENUITEM_PROP_VISIBLE, get_children () != null); - } - - private void connect_service (string device, bool connect) - { - client.connect_service.begin (device, connect, null, (object, result) => - { - var connected = false; - try - { - connected = client.connect_service.end (result); - } - catch (Error e) - { - warning ("Failed to connect service: %s", e.message); - } - }); - } -} - -private void set_up_new_device () -{ - try - { - Process.spawn_command_line_async ("bluetooth-wizard"); - } - catch (GLib.SpawnError e) - { - warning ("Failed to open bluetooth-wizard: %s", e.message); - } -} - -private void show_control_center (string panel) -{ - try - { - Process.spawn_command_line_async ("gnome-control-center %s".printf (panel)); - } - catch (GLib.SpawnError e) - { - warning ("Failed to open control center: %s", e.message); - } -} - -[DBus (name = "com.canonical.indicator.bluetooth.service")] -private class BluetoothService : Object -{ - internal bool _visible = false; - public bool visible - { - get { return _visible; } - } - internal string _icon_name = "bluetooth-active"; - public string icon_name - { - get { return _icon_name; } - } - internal string _accessible_description = _("Bluetooth"); - public string accessible_description - { - get { return _accessible_description; } - } -} diff --git a/src/indicator-bluetooth.vala b/src/indicator-bluetooth.vala deleted file mode 100644 index bfbbc9f..0000000 --- a/src/indicator-bluetooth.vala +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2012-2013 Canonical Ltd. - * Author: Robert Ancell - * - * 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 of the License. - * See http://www.gnu.org/copyleft/gpl.html the full text of the license. - */ - -public class BluetoothIndicator : Indicator.Object -{ - private Indicator.ServiceManager service; - private Gtk.Image image; - private DbusmenuGtk.Menu menu; - private BluetoothService proxy; - private string accessible_description = _("Bluetooth: On"); - - construct - { - service = new Indicator.ServiceManager ("com.canonical.indicator.bluetooth"); - service.connection_change.connect (connection_change_cb); - menu = new DbusmenuGtk.Menu ("com.canonical.indicator.bluetooth", "/com/canonical/indicator/bluetooth/menu"); - image = Indicator.image_helper ("bluetooth-active"); - image.visible = true; - - var menu_client = menu.get_client (); - menu_client.add_type_handler_full ("x-canonical-switch", new_switch_cb); - - /* Hide until ready */ - set_visible (false); - } - - private bool new_switch_cb (Dbusmenu.Menuitem newitem, Dbusmenu.Menuitem parent, Dbusmenu.Client client) - { - var item = new Switch (newitem); - (client as DbusmenuGtk.Client).newitem_base (newitem, item, parent); - return true; - } - - public override unowned Gtk.Image get_image () - { - return image; - } - - public override unowned Gtk.Menu get_menu () - { - return menu; - } - - public override unowned string get_accessible_desc () - { - return accessible_description; - } - - private void connection_change_cb (bool connected) - { - if (!connected) - return; - - // FIXME: Set proxy to null on disconnect? - // FIXME: Use Cancellable to cancel existing connection - if (proxy == null) - { - Bus.get_proxy.begin (BusType.SESSION, - "com.canonical.indicator.bluetooth", - "/com/canonical/indicator/bluetooth/service", - DBusProxyFlags.NONE, null, (object, result) => - { - try - { - proxy = Bus.get_proxy.end (result); - proxy.g_properties_changed.connect (server_properties_changed_cb); - server_properties_changed_cb (); - } - catch (IOError e) - { - warning ("Failed to connect to bluetooth service: %s", e.message); - } - }); - } - } - - private void server_properties_changed_cb () - { - set_visible (proxy.visible); - Indicator.image_helper_update (image, proxy.icon_name); - accessible_description = proxy.accessible_description; - } -} - -public class Switch : Ido.SwitchMenuItem -{ - public Dbusmenu.Menuitem menuitem; - public new Gtk.Label label; - private bool updating_switch = false; - - public Switch (Dbusmenu.Menuitem menuitem) - { - this.menuitem = menuitem; - label = new Gtk.Label (""); - label.visible = true; - content_area.add (label); - - /* Be the first listener to the activate signal so we can stop it - * emitting when we change the state. Without this you get feedback loops */ - activate.connect (() => - { - if (updating_switch) - Signal.stop_emission_by_name (this, "activate"); - }); - - menuitem.property_changed.connect ((mi, prop, value) => { update (); }); - update (); - } - - private void update () - { - updating_switch = true; - label.label = menuitem.property_get (Dbusmenu.MENUITEM_PROP_LABEL); - active = menuitem.property_get_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE) == Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED; - updating_switch = false; - } -} - -[DBus (name = "com.canonical.indicator.bluetooth.service")] -public interface BluetoothService : DBusProxy -{ - public abstract bool visible { owned get; } - public abstract string icon_name { owned get; } - public abstract string accessible_description { owned get; } -} - -public static string get_version () -{ - return Indicator.VERSION; -} - -public static GLib.Type get_type () -{ - return typeof (BluetoothIndicator); -} diff --git a/src/main.vala b/src/main.vala index 79683b2..1df55c3 100644 --- a/src/main.vala +++ b/src/main.vala @@ -7,21 +7,8 @@ main (string[] args) Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.GNOMELOCALEDIR); Intl.textdomain (Config.GETTEXT_PACKAGE); - var loop = new MainLoop (); + var service = new BluetoothIndicator (); + service.run (); - BluetoothIndicator indicator; - try - { - indicator = new BluetoothIndicator (); - } - catch (Error e) - { - warning ("Failed to start bluetooth indicator service: %s", e.message); - return Posix.EXIT_FAILURE; - } - - loop.run (); return Posix.EXIT_SUCCESS; } - //var service = new IndicatorSound.Service (); - //return service.run (); diff --git a/src/menu.vala b/src/menu.vala new file mode 100644 index 0000000..a2c5449 --- /dev/null +++ b/src/menu.vala @@ -0,0 +1,53 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +class BluetoothMenu: Object +{ + protected Menu root; + protected Menu menu; + + public virtual void add_actions_to_group (SimpleActionGroup group) + { + } + + public BluetoothMenu (string profile) + { + this.menu = new Menu (); + + var root_item = new MenuItem (null, "indicator.root-" + profile); + root_item.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.root"); + root_item.set_submenu (this.menu); + + this.root = new Menu (); + this.root.append_item (root_item); + } + + public void export (DBusConnection connection, string object_path) + { + try + { + message ("exporting on %s", object_path); + connection.export_menu_model (object_path, this.root); + } + catch (Error e) + { + critical ("%s", e.message); + } + } +} diff --git a/src/phone.vala b/src/phone.vala new file mode 100644 index 0000000..2abcf11 --- /dev/null +++ b/src/phone.vala @@ -0,0 +1,76 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +class PhoneMenu: BluetoothMenu +{ + private Action[] actions; + + public override void add_actions_to_group (SimpleActionGroup group) + { + base.add_actions_to_group (group); + + for (var i=0; i { + try { + Process.spawn_command_line_async ("system-settings bluetooth"); + } catch (Error e) { + warning ("unable to launch settings: %s", e.message); + } + }); + + return action; + } + + protected Variant action_state_for_root () + { + var label = "Hello World"; // FIXME + var a11y = "Hello World"; // FIXME + var visible = true; // FIXME + + string icon_name = "bluetooth"; // FIXME: enabled, disabled, connected, etc. + var icon = new ThemedIcon.with_default_fallbacks (icon_name); + + var builder = new VariantBuilder (new VariantType ("a{sv}")); + builder.add ("{sv}", "visible", new Variant ("b", visible)); + builder.add ("{sv}", "label", new Variant ("s", label)); + builder.add ("{sv}", "accessible-desc", new Variant ("s", a11y)); + builder.add ("{sv}", "icon", icon.serialize()); + return builder.end (); + } +} diff --git a/src/service.vala b/src/service.vala new file mode 100644 index 0000000..b6ae0f3 --- /dev/null +++ b/src/service.vala @@ -0,0 +1,470 @@ +/* + * Copyright (C) 2012-2013 Canonical Ltd. + * Author: Robert Ancell + * + * 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 of the License. + * See http://www.gnu.org/copyleft/gpl.html the full text of the license. + */ + +public class BluetoothIndicator +{ + private MainLoop loop; + private SimpleActionGroup actions; + private HashTable menus; + + private DBusConnection bus; + private Indicator.Service indicator_service; + private Dbusmenu.Server menu_server; + private BluetoothService bluetooth_service; + private GnomeBluetooth.Client client; + private GnomeBluetooth.Killswitch killswitch; + private bool updating_killswitch = false; + private Dbusmenu.Menuitem enable_item; + private Dbusmenu.Menuitem visible_item; + private bool updating_visible = false; + private Dbusmenu.Menuitem devices_separator; + private List device_items; + private Dbusmenu.Menuitem menu; + + public BluetoothIndicator () throws Error + { + var phone = new PhoneMenu (); + var desktop = new DesktopMenu (); + + this.menus = new HashTable (str_hash, str_equal); + this.menus.insert ("phone", phone); + this.menus.insert ("desktop", desktop); + + this.actions = new SimpleActionGroup (); + phone.add_actions_to_group (this.actions); + desktop.add_actions_to_group (this.actions); + } + + private void init_for_bus (DBusConnection bus) + { + this.bus = bus; + + indicator_service = new Indicator.Service ("com.canonical.indicator.bluetooth.old"); + menu_server = new Dbusmenu.Server ("/com/canonical/indicator/bluetooth/menu"); + + bluetooth_service = new BluetoothService (); + bus.register_object ("/com/canonical/indicator/bluetooth/service", bluetooth_service); + + killswitch = new GnomeBluetooth.Killswitch (); + killswitch.state_changed.connect (killswitch_state_changed_cb); + + client = new GnomeBluetooth.Client (); + + menu = new Dbusmenu.Menuitem (); + menu_server.set_root (menu); + + enable_item = new Dbusmenu.Menuitem (); + enable_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Bluetooth")); + enable_item.property_set (Dbusmenu.MENUITEM_PROP_TYPE, "x-canonical-switch"); + enable_item.item_activated.connect (() => + { + if (updating_killswitch) + return; + if (killswitch.state == GnomeBluetooth.KillswitchState.UNBLOCKED) + killswitch.state = GnomeBluetooth.KillswitchState.SOFT_BLOCKED; + else + killswitch.state = GnomeBluetooth.KillswitchState.UNBLOCKED; + }); + menu.child_append (enable_item); + + visible_item = new Dbusmenu.Menuitem (); + visible_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Visible")); + visible_item.property_set (Dbusmenu.MENUITEM_PROP_TYPE, "x-canonical-switch"); + bool discoverable; + client.get ("default-adapter-discoverable", out discoverable); + visible_item.property_set_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE, discoverable ? Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED : Dbusmenu.MENUITEM_TOGGLE_STATE_UNCHECKED); + client.notify["default-adapter-discoverable"].connect (() => + { + updating_visible = true; + bool is_discoverable; + client.get ("default-adapter-discoverable", out is_discoverable); + visible_item.property_set_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE, is_discoverable ? Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED : Dbusmenu.MENUITEM_TOGGLE_STATE_UNCHECKED); + updating_visible = false; + }); + visible_item.item_activated.connect (() => + { + if (updating_visible) + return; + client.set ("default-adapter-discoverable", visible_item.property_get_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE) != Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED); + }); + menu.child_append (visible_item); + + devices_separator = new Dbusmenu.Menuitem (); + devices_separator.property_set (Dbusmenu.MENUITEM_PROP_TYPE, Dbusmenu.CLIENT_TYPES_SEPARATOR); + menu.child_append (devices_separator); + + device_items = new List (); + + client.model.row_inserted.connect (device_changed_cb); + client.model.row_changed.connect (device_changed_cb); + client.model.row_deleted.connect (device_removed_cb); + Gtk.TreeIter iter; + var have_iter = client.model.get_iter_first (out iter); + while (have_iter) + { + Gtk.TreeIter child_iter; + var have_child_iter = client.model.iter_children (out child_iter, iter); + while (have_child_iter) + { + device_changed_cb (null, child_iter); + have_child_iter = client.model.iter_next (ref child_iter); + } + have_iter = client.model.iter_next (ref iter); + } + + var sep = new Dbusmenu.Menuitem (); + sep.property_set (Dbusmenu.MENUITEM_PROP_TYPE, Dbusmenu.CLIENT_TYPES_SEPARATOR); + menu.child_append (sep); + + var item = new Dbusmenu.Menuitem (); + item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Set Up New Device…")); + item.item_activated.connect (() => { set_up_new_device (); }); + menu.child_append (item); + + item = new Dbusmenu.Menuitem (); + item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Bluetooth Settings…")); + item.item_activated.connect (() => { show_control_center ("bluetooth"); }); + menu.child_append (item); + + killswitch_state_changed_cb (killswitch.state); + + client.adapter_model.row_inserted.connect (update_visible); + client.adapter_model.row_deleted.connect (update_visible); + update_visible (); + } + + public int run () + { + if (this.loop != null) + { + warning ("service is already running"); + return 1; + } + + Bus.own_name (BusType.SESSION, + "com.canonical.indicator.bluetooth", + BusNameOwnerFlags.NONE, + this.on_bus_acquired, + null, + this.on_name_lost); + + this.loop = new MainLoop (null, false); + this.loop.run (); + return 0; + } + + void on_bus_acquired (DBusConnection connection, string name) + { + stdout.printf ("bus acquired: %s\n", name); + + init_for_bus (connection); + + try + { + connection.export_action_group ("/com/canonical/indicator/bluetooth", this.actions); + } + catch (Error e) + { + critical ("%s", e.message); + } + + this.menus.@foreach ( (profile, menu) => menu.export (connection, @"/com/canonical/indicator/bluetooth/$profile")); + } + + void on_name_lost (DBusConnection connection, string name) + { + stdout.printf ("name lost: %s\n", name); + this.loop.quit (); + } + + + private BluetoothMenuItem? find_menu_item (string address) + { + foreach (var item in device_items) + if (item.address == address) + return item; + + return null; + } + + private void device_changed_cb (Gtk.TreePath? path, Gtk.TreeIter iter) + { + /* Ignore adapters */ + Gtk.TreeIter parent_iter; + if (!client.model.iter_parent (out parent_iter, iter)) + return; + + DBusProxy proxy; + string address; + string alias; + GnomeBluetooth.Type type; + string icon; + bool connected; + HashTable services; + string[] uuids; + client.model.get (iter, + GnomeBluetooth.Column.PROXY, out proxy, + GnomeBluetooth.Column.ADDRESS, out address, + GnomeBluetooth.Column.ALIAS, out alias, + GnomeBluetooth.Column.TYPE, out type, + GnomeBluetooth.Column.ICON, out icon, + GnomeBluetooth.Column.CONNECTED, out connected, + GnomeBluetooth.Column.SERVICES, out services, + GnomeBluetooth.Column.UUIDS, out uuids); + + /* Skip if haven't actually got any information yet */ + if (proxy == null) + return; + + /* Find or create menu item */ + var item = find_menu_item (address); + if (item == null) + { + item = new BluetoothMenuItem (client, address); + item.property_set_bool (Dbusmenu.MENUITEM_PROP_VISIBLE, killswitch.state == GnomeBluetooth.KillswitchState.UNBLOCKED); + var last_item = devices_separator as Dbusmenu.Menuitem; + if (device_items != null) + last_item = device_items.last ().data; + device_items.append (item); + menu.child_add_position (item, last_item.get_position (menu) + 1); + } + + item.update (type, proxy, alias, icon, connected, services, uuids); + } + + private void update_visible () + { + bluetooth_service._visible = client.adapter_model.iter_n_children (null) > 0;// && settings.get_boolean ("visible"); + var builder = new VariantBuilder (VariantType.ARRAY); + builder.add ("{sv}", "Visible", new Variant.boolean (bluetooth_service._visible)); + try + { + var properties = new Variant ("(sa{sv}as)", "com.canonical.indicator.bluetooth.service", builder, null); + bus.emit_signal (null, + "/com/canonical/indicator/bluetooth/service", + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + properties); + } + catch (Error e) + { + warning ("Failed to emit signal: %s", e.message); + } + } + + private void device_removed_cb (Gtk.TreePath path) + { + Gtk.TreeIter iter; + if (!client.model.get_iter (out iter, path)) + return; + + string address; + client.model.get (iter, GnomeBluetooth.Column.ADDRESS, out address); + + var item = find_menu_item (address); + if (item == null) + return; + + device_items.remove (item); + menu.child_delete (item); + } + + private void killswitch_state_changed_cb (GnomeBluetooth.KillswitchState state) + { + updating_killswitch = true; + + var enabled = state == GnomeBluetooth.KillswitchState.UNBLOCKED; + + bluetooth_service._icon_name = enabled ? "bluetooth-active" : "bluetooth-disabled"; + bluetooth_service._accessible_description = enabled ? _("Bluetooth: On") : _("Bluetooth: Off"); + + var builder = new VariantBuilder (VariantType.ARRAY); + builder.add ("{sv}", "IconName", new Variant.string (bluetooth_service._icon_name)); + builder.add ("{sv}", "AccessibleDescription", new Variant.string (bluetooth_service._accessible_description)); + try + { + var properties = new Variant ("(sa{sv}as)", "com.canonical.indicator.bluetooth.service", builder, null); + bus.emit_signal (null, + "/com/canonical/indicator/bluetooth/service", + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + properties); + } + catch (Error e) + { + warning ("Failed to emit signal: %s", e.message); + } + + enable_item.property_set_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE, enabled ? Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED : Dbusmenu.MENUITEM_TOGGLE_STATE_UNCHECKED); + + /* Disable devices when locked */ + visible_item.property_set_bool (Dbusmenu.MENUITEM_PROP_VISIBLE, enabled); + devices_separator.property_set_bool (Dbusmenu.MENUITEM_PROP_VISIBLE, enabled); + foreach (var item in device_items) + item.property_set_bool (Dbusmenu.MENUITEM_PROP_VISIBLE, enabled && item.get_children () != null); + + updating_killswitch = false; + } +} + +private class BluetoothMenuItem : Dbusmenu.Menuitem +{ + private GnomeBluetooth.Client client; + public string address; + private Dbusmenu.Menuitem? connect_item = null; + private bool make_submenu = false; + + public BluetoothMenuItem (GnomeBluetooth.Client client, string address) + { + this.client = client; + this.address = address; + } + + public void update (GnomeBluetooth.Type type, DBusProxy proxy, string alias, string icon, bool connected, HashTable? services, string[] uuids) + { + property_set (Dbusmenu.MENUITEM_PROP_LABEL, alias); + property_set (Dbusmenu.MENUITEM_PROP_ICON_NAME, icon); + if (connect_item != null) + connect_item.property_set_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE, connected ? Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED : Dbusmenu.MENUITEM_TOGGLE_STATE_UNCHECKED); + + /* FIXME: Not sure if the GUI elements below can change over time */ + if (make_submenu) + return; + make_submenu = true; + + if (services != null) + { + connect_item = new Dbusmenu.Menuitem (); + connect_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Connection")); + connect_item.property_set (Dbusmenu.MENUITEM_PROP_TYPE, "x-canonical-switch"); + connect_item.property_set_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE, connected ? Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED : Dbusmenu.MENUITEM_TOGGLE_STATE_UNCHECKED); + connect_item.item_activated.connect (() => { connect_service (proxy.get_object_path (), connect_item.property_get_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE) != Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED); }); + child_append (connect_item); + } + + var can_send = false; + var can_browse = false; + if (uuids != null) + { + for (var i = 0; uuids[i] != null; i++) + { + if (uuids[i] == "OBEXObjectPush") + can_send = true; + if (uuids[i] == "OBEXFileTransfer") + can_browse = true; + } + } + + if (can_send) + { + var send_item = new Dbusmenu.Menuitem (); + send_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Send files…")); + send_item.item_activated.connect (() => { GnomeBluetooth.send_to_address (address, alias); }); + child_append (send_item); + } + + if (can_browse) + { + var browse_item = new Dbusmenu.Menuitem (); + browse_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Browse files…")); + browse_item.item_activated.connect (() => { GnomeBluetooth.browse_address (null, address, Gdk.CURRENT_TIME, null); }); + child_append (browse_item); + } + + switch (type) + { + case GnomeBluetooth.Type.KEYBOARD: + var keyboard_item = new Dbusmenu.Menuitem (); + keyboard_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Keyboard Settings…")); + keyboard_item.item_activated.connect (() => { show_control_center ("keyboard"); }); + child_append (keyboard_item); + break; + + case GnomeBluetooth.Type.MOUSE: + case GnomeBluetooth.Type.TABLET: + var mouse_item = new Dbusmenu.Menuitem (); + mouse_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Mouse and Touchpad Settings…")); + mouse_item.item_activated.connect (() => { show_control_center ("mouse"); }); + child_append (mouse_item); + break; + + case GnomeBluetooth.Type.HEADSET: + case GnomeBluetooth.Type.HEADPHONES: + case GnomeBluetooth.Type.OTHER_AUDIO: + var sound_item = new Dbusmenu.Menuitem (); + sound_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Sound Settings…")); + sound_item.item_activated.connect (() => { show_control_center ("sound"); }); + child_append (sound_item); + break; + } + + property_set_bool (Dbusmenu.MENUITEM_PROP_VISIBLE, get_children () != null); + } + + private void connect_service (string device, bool connect) + { + client.connect_service.begin (device, connect, null, (object, result) => + { + var connected = false; + try + { + connected = client.connect_service.end (result); + } + catch (Error e) + { + warning ("Failed to connect service: %s", e.message); + } + }); + } +} + +private void set_up_new_device () +{ + try + { + Process.spawn_command_line_async ("bluetooth-wizard"); + } + catch (GLib.SpawnError e) + { + warning ("Failed to open bluetooth-wizard: %s", e.message); + } +} + +private void show_control_center (string panel) +{ + try + { + Process.spawn_command_line_async ("gnome-control-center %s".printf (panel)); + } + catch (GLib.SpawnError e) + { + warning ("Failed to open control center: %s", e.message); + } +} + +[DBus (name = "com.canonical.indicator.bluetooth.service")] +private class BluetoothService : Object +{ + internal bool _visible = false; + public bool visible + { + get { return _visible; } + } + internal string _icon_name = "bluetooth-active"; + public string icon_name + { + get { return _icon_name; } + } + internal string _accessible_description = _("Bluetooth"); + public string accessible_description + { + get { return _accessible_description; } + } +} -- cgit v1.2.3 From cf894302767d5ec483b5f8002bb796a284895bbf Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 1 Aug 2013 18:21:05 -0500 Subject: add a bluetooth backend to track bluetooth being enabled, being hard/soft blocked, and its devices. --- src/Makefile.am | 5 + src/bluetooth.vala | 53 ++++++++++ src/bluez.vala | 97 +++++++++++++++++ src/desktop.vala | 121 +++++++++++++++------ src/killswitch.vala | 147 ++++++++++++++++++++++++++ src/libido3-0.1.vapi | 10 -- src/main.vala | 4 +- src/org-bluez.vala | 293 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/phone.vala | 2 +- src/service.vala | 21 ++-- 10 files changed, 698 insertions(+), 55 deletions(-) create mode 100644 src/bluetooth.vala create mode 100644 src/bluez.vala create mode 100644 src/killswitch.vala delete mode 100644 src/libido3-0.1.vapi create mode 100644 src/org-bluez.vala (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index ed2c976..720c981 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,10 +2,14 @@ pkglibexec_PROGRAMS = indicator-bluetooth-service indicator_bluetooth_service_SOURCES = \ indicator3-0.4.vapi \ + org-bluez.vala \ + bluetooth.vala \ + bluez.vala \ desktop.vala \ main.vala \ menu.vala \ phone.vala \ + killswitch.vala \ service.vala indicator_bluetooth_service_VALAFLAGS = \ @@ -14,6 +18,7 @@ indicator_bluetooth_service_VALAFLAGS = \ --vapidir=./ \ --pkg gnome-bluetooth-1.0 \ --pkg config \ + --pkg rfkill \ --pkg posix \ --pkg glib-2.0 \ --pkg gtk+-3.0 \ diff --git a/src/bluetooth.vala b/src/bluetooth.vala new file mode 100644 index 0000000..ad5ee09 --- /dev/null +++ b/src/bluetooth.vala @@ -0,0 +1,53 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +public class Bluetooth: Object +{ + /*** + **** Properties + ***/ + + public bool discoverable { get; protected set; default = false; } + public virtual void try_set_discoverable (bool b) {} + + public bool powered { get; protected set; default = false; } + + public bool blocked { get; protected set; default = true; } + public virtual void try_set_blocked (bool b) { + kill_switch.try_set_blocked (b); + } + + /*** + **** Killswitch Implementation + ***/ + + protected KillSwitch kill_switch; + + public Bluetooth (KillSwitch kill_switch) + { + this.kill_switch = kill_switch; + + message ("changing blocked to %d", (int)!this.kill_switch.blocked); + blocked = this.kill_switch.blocked; + kill_switch.notify["blocked"].connect (() => { + message ("bluetooth changing blocked to %d", (int)kill_switch.blocked); + this.blocked = kill_switch.blocked; + }); + } +} diff --git a/src/bluez.vala b/src/bluez.vala new file mode 100644 index 0000000..178cc57 --- /dev/null +++ b/src/bluez.vala @@ -0,0 +1,97 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +public class Bluez: Bluetooth +{ + private org.bluez.Manager manager; + private org.bluez.Adapter default_adapter; + + public Bluez (KillSwitch kill_switch) + { + string default_adapter_object_path = null; + + base (kill_switch); + + try + { + manager = Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", "/"); + + manager.default_adapter_changed.connect ((object_path) => on_default_adapter_changed (object_path)); + default_adapter_object_path = manager.default_adapter (); + } + catch (Error e) + { + critical ("%s", e.message); + } + + on_default_adapter_changed (default_adapter_object_path); + } + + private void on_default_adapter_changed (string? object_path) + { + if (object_path != null) try + { + message ("using default adapter at %s", object_path); + this.default_adapter = Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", object_path); + this.default_adapter.property_changed.connect(() => this.on_default_adapter_properties_changed()); + } + catch (Error e) + { + critical ("%s", e.message); + } + + this.on_default_adapter_properties_changed (); + } + + private void on_default_adapter_properties_changed () + { + bool is_discoverable = false; + bool is_powered = false; + + if (this.default_adapter != null) try + { + var properties = this.default_adapter.get_properties(); + + var v = properties.lookup("Discoverable"); + is_discoverable = (v != null) && v.get_boolean (); + + v = properties.lookup("Powered"); + is_powered = (v != null) && v.get_boolean (); + } + catch (Error e) + { + critical ("%s", e.message); + } + + this.powered = is_powered; + this.discoverable = is_discoverable; + } + + public override void try_set_discoverable (bool b) + { + if (discoverable != b) try + { + this.default_adapter.set_property ("Discoverable", new Variant.boolean(b)); + } + catch (Error e) + { + critical ("%s", e.message); + } + } +} diff --git a/src/desktop.vala b/src/desktop.vala index bd005f5..351770a 100644 --- a/src/desktop.vala +++ b/src/desktop.vala @@ -20,80 +20,133 @@ class DesktopMenu: BluetoothMenu { private Settings settings; + private Bluetooth bluetooth; - private Action[] actions; + private SimpleAction root_action; + private Action[] all_actions; public override void add_actions_to_group (SimpleActionGroup group) { base.add_actions_to_group (group); - for (var i=0; i { message("visible toggled"); }); - this.actions = {}; - this.actions += new SimpleAction.stateful ("root-desktop", null, action_state_for_root()); - this.actions += create_settings_action (); - this.actions += create_wizard_action (); + this.root_action = new SimpleAction.stateful ("root-desktop", null, action_state_for_root()); + + this.all_actions = {}; + this.all_actions += this.root_action; + this.all_actions += create_enabled_action (bluetooth); + this.all_actions += create_discoverable_action (bluetooth); + this.all_actions += create_settings_action (); + this.all_actions += create_wizard_action (); + + bluetooth.notify.connect (() => this.update_root_action_state()); + settings.changed["visible"].connect (()=> this.update_root_action_state()); + + Menu section; + MenuItem item; - var section = new Menu (); + section = new Menu (); + item = new MenuItem ("Bluetooth", "indicator.desktop-enabled"); + item.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.switch"); + section.append_item (item); + item = new MenuItem ("Visible", "indicator.desktop-discoverable"); + item.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.switch"); + section.append_item (item); + this.menu.append_section (null, section); + + section = new Menu (); section.append (_("Set Up New Device…"), "indicator.desktop-wizard"); section.append (_("Bluetooth Settings…"), "indicator.desktop-settings"); this.menu.append_section (null, section); } - Action create_wizard_action () + Action create_enabled_action (Bluetooth bluetooth) { - var action = new SimpleAction ("desktop-wizard", null); + var action = new SimpleAction.stateful ("desktop-enabled", null, !bluetooth.blocked); + action.activate.connect (() => action.set_state (!action.get_state().get_boolean())); + action.notify["state"].connect (() => bluetooth.try_set_blocked (!action.get_state().get_boolean())); + bluetooth.notify["blocked"].connect (() => action.set_state (!bluetooth.blocked)); + return action; + } - action.activate.connect ((action, param) => { - try { - Process.spawn_command_line_async ("bluetooth-wizard"); - } catch (Error e) { - warning ("unable to launch settings: %s", e.message); - } - }); + Action create_discoverable_action (Bluetooth bluetooth) + { + var action = new SimpleAction.stateful ("desktop-discoverable", null, bluetooth.discoverable); + action.set_enabled (bluetooth.powered); + action.activate.connect (() => action.set_state (!action.get_state().get_boolean())); + action.notify["state"].connect (() => bluetooth.try_set_discoverable (action.get_state().get_boolean())); + bluetooth.notify["discoverable"].connect (() => action.set_state (bluetooth.discoverable)); + bluetooth.notify["powered"].connect (() => action.set_enabled (bluetooth.powered)); + return action; + } + void spawn_command_line_async (string command) + { + try { + Process.spawn_command_line_async (command); + } catch (Error e) { + warning ("unable to launch '%s': %s", command, e.message); + } + } + + Action create_wizard_action () + { + var action = new SimpleAction ("desktop-wizard", null); + action.activate.connect (() => spawn_command_line_async ("bluetooth-wizard")); return action; } Action create_settings_action () { var action = new SimpleAction ("desktop-settings", null); - - action.activate.connect ((action, param) => { - try { - Process.spawn_command_line_async ("gnome-control-center bluetooth"); - } catch (Error e) { - warning ("unable to launch settings: %s", e.message); - } - }); - + action.activate.connect (() => spawn_command_line_async ("gnome-control-center bluetooth")); return action; } protected Variant action_state_for_root () { - var label = "Hello"; // FIXME - var a11y = "Hello"; // FIXME - var visible = true; // FIXME + bool blocked = bluetooth.blocked; + bool powered = bluetooth.powered; + + settings.changed["visible"].connect (()=> this.update_root_action_state()); + + bool visible = powered && settings.get_boolean("visible"); + + string a11y; + string icon_name; + if (powered && !blocked) + { + a11y = "Bluetooth (on)"; + icon_name = "bluetooth-active"; + } + else + { + a11y = "Bluetooth (off)"; + icon_name = "bluetooth-disabled"; + } - string icon_name = "bluetooth-active"; // FIXME: enabled, disabled, connected, etc. -//indicator-bluetooth-service.vala: bluetooth_service._icon_name = enabled ? "bluetooth-active" : "bluetooth-disabled"; var icon = new ThemedIcon.with_default_fallbacks (icon_name); var builder = new VariantBuilder (new VariantType ("a{sv}")); builder.add ("{sv}", "visible", new Variant ("b", visible)); - builder.add ("{sv}", "label", new Variant ("s", label)); builder.add ("{sv}", "accessible-desc", new Variant ("s", a11y)); builder.add ("{sv}", "icon", icon.serialize()); return builder.end (); } + + void update_root_action_state () + { + root_action.set_state (action_state_for_root ()); + } } diff --git a/src/killswitch.vala b/src/killswitch.vala new file mode 100644 index 0000000..92b1a3c --- /dev/null +++ b/src/killswitch.vala @@ -0,0 +1,147 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +/** + * Monitors whether or not bluetooth is blocked, + * either by software (e.g., a session configuration setting) + * or by hardware (e.g., user disabled it via a physical switch on her laptop) + */ +public class KillSwitch: Object +{ + public bool blocked { get; protected set; default = false; } + + public void try_set_blocked (bool blocked) + { + return_if_fail (this.blocked != blocked); + + // write a 'soft kill' event to fkill + var event = Linux.RfKillEvent() { + op = Linux.RfKillOp.CHANGE_ALL, + type = Linux.RfKillType.BLUETOOTH, + soft = (uint8)blocked + }; + + var bwritten = Posix.write (fd, &event, sizeof(Linux.RfKillEvent)); + if (bwritten == -1) + warning ("Could not write rfkill event: %s", strerror(errno)); + } + + /*** + **** Past this point, it's all RfKill implementation details... + ***/ + + private class Entry + { + public uint32 idx; + public Linux.RfKillType type; + public bool soft; + public bool hard; + } + + private HashTable entries; + private int fd; + private IOChannel channel; + private uint watch; + + private bool calculate_blocked () + { + foreach (Entry entry in entries.get_values()) + if (entry.soft || entry.hard) + return true; + + return false; + } + + ~KillSwitch () + { + Source.remove (watch); + Posix.close (fd); + } + + public KillSwitch () + { + entries = new HashTable(direct_hash, direct_equal); + + var path = "/dev/rfkill"; + fd = Posix.open (path, Posix.O_RDWR | Posix.O_NONBLOCK ); + message ("fd is %d", fd); + if (fd == -1) + { + warning (@"Can't open $path: $(strerror(errno)); KillSwitch disable"); + } + else + { + // read everything that's already there, then watch for more + while (read_event()); + channel = new IOChannel.unix_new (fd); + watch = channel.add_watch (IOCondition.IN, on_channel_event); + } + } + + private bool on_channel_event (IOChannel source, IOCondition condition) + { + read_event (); + return true; + } + + private bool read_event () + { + assert (fd != -1); + + var event = Linux.RfKillEvent(); + var n = sizeof (Linux.RfKillEvent); + var bytesread = Posix.read (fd, &event, n); + + if (bytesread == n) + { + process_event (event); + return true; + } + + return false; + } + + private void process_event (Linux.RfKillEvent event) + { + // we only want things that affect bluetooth + if ((event.type != Linux.RfKillType.ALL) && + (event.type != Linux.RfKillType.BLUETOOTH)) + return; + + switch (event.op) + { + case Linux.RfKillOp.CHANGE: + case Linux.RfKillOp.ADD: + Entry entry = new Entry (); + entry.idx = event.idx; + entry.type = event.type; + entry.soft = event.soft != 0; + entry.hard = event.hard != 0; + entries.insert (entry.idx, entry); + break; + + case Linux.RfKillOp.DEL: + entries.remove (event.idx); + break; + } + + // update the 'blocked' property + blocked = calculate_blocked (); + } +} diff --git a/src/libido3-0.1.vapi b/src/libido3-0.1.vapi deleted file mode 100644 index e6a8953..0000000 --- a/src/libido3-0.1.vapi +++ /dev/null @@ -1,10 +0,0 @@ -namespace Ido -{ - [CCode (cheader_filename = "libido/idoswitchmenuitem.h")] - public class SwitchMenuItem : Gtk.CheckMenuItem - { - [CCode (has_construct_function = false)] - public SwitchMenuItem (); - public Gtk.Container content_area { get; } - } -} diff --git a/src/main.vala b/src/main.vala index 1df55c3..7af617d 100644 --- a/src/main.vala +++ b/src/main.vala @@ -7,7 +7,9 @@ main (string[] args) Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.GNOMELOCALEDIR); Intl.textdomain (Config.GETTEXT_PACKAGE); - var service = new BluetoothIndicator (); + var bluetooth = new Bluez (new KillSwitch ()); + var service = new BluetoothIndicator (bluetooth); + service.run (); return Posix.EXIT_SUCCESS; diff --git a/src/org-bluez.vala b/src/org-bluez.vala new file mode 100644 index 0000000..49c8e4d --- /dev/null +++ b/src/org-bluez.vala @@ -0,0 +1,293 @@ +/* Generated by vala-dbus-binding-tool 0.4.0. Do not modify! */ +/* Generated with: vala-dbus-binding-tool --gdbus --directory=. */ +using GLib; + +namespace org { + + namespace bluez { + + [DBus (name = "org.bluez.Manager", timeout = 120000)] + public interface Manager : GLib.Object { + + [DBus (name = "GetProperties")] + public abstract GLib.HashTable get_properties() throws DBusError, IOError; + + [DBus (name = "DefaultAdapter")] + public abstract GLib.ObjectPath default_adapter() throws DBusError, IOError; + + [DBus (name = "FindAdapter")] + public abstract GLib.ObjectPath find_adapter(string pattern) throws DBusError, IOError; + + [DBus (name = "ListAdapters")] + public abstract GLib.ObjectPath[] list_adapters() throws DBusError, IOError; + + [DBus (name = "PropertyChanged")] + public signal void property_changed(string name, GLib.Variant value); + + [DBus (name = "AdapterAdded")] + public signal void adapter_added(GLib.ObjectPath adapter); + + [DBus (name = "AdapterRemoved")] + public signal void adapter_removed(GLib.ObjectPath adapter); + + [DBus (name = "DefaultAdapterChanged")] + public signal void default_adapter_changed(GLib.ObjectPath adapter); + } + + [DBus (name = "org.bluez.Manager", timeout = 120000)] + public interface ManagerSync : GLib.Object { + + [DBus (name = "GetProperties")] + public abstract GLib.HashTable get_properties() throws DBusError, IOError; + + [DBus (name = "DefaultAdapter")] + public abstract GLib.ObjectPath default_adapter() throws DBusError, IOError; + + [DBus (name = "FindAdapter")] + public abstract GLib.ObjectPath find_adapter(string pattern) throws DBusError, IOError; + + [DBus (name = "ListAdapters")] + public abstract GLib.ObjectPath[] list_adapters() throws DBusError, IOError; + + [DBus (name = "PropertyChanged")] + public signal void property_changed(string name, GLib.Variant value); + + [DBus (name = "AdapterAdded")] + public signal void adapter_added(GLib.ObjectPath adapter); + + [DBus (name = "AdapterRemoved")] + public signal void adapter_removed(GLib.ObjectPath adapter); + + [DBus (name = "DefaultAdapterChanged")] + public signal void default_adapter_changed(GLib.ObjectPath adapter); + } + + [DBus (name = "org.bluez.Adapter", timeout = 120000)] + public interface Adapter : GLib.Object { + + [DBus (name = "GetProperties")] + public abstract GLib.HashTable get_properties() throws DBusError, IOError; + + [DBus (name = "SetProperty")] + public abstract void set_property(string name, GLib.Variant value) throws DBusError, IOError; + + [DBus (name = "RequestSession")] + public abstract void request_session() throws DBusError, IOError; + + [DBus (name = "ReleaseSession")] + public abstract void release_session() throws DBusError, IOError; + + [DBus (name = "StartDiscovery")] + public abstract void start_discovery() throws DBusError, IOError; + + [DBus (name = "StopDiscovery")] + public abstract void stop_discovery() throws DBusError, IOError; + + [DBus (name = "ListDevices")] + public abstract GLib.ObjectPath[] list_devices() throws DBusError, IOError; + + [DBus (name = "CreateDevice")] + public abstract GLib.ObjectPath create_device(string address) throws DBusError, IOError; + + [DBus (name = "CreatePairedDevice")] + public abstract GLib.ObjectPath create_paired_device(string address, GLib.ObjectPath agent, string capability) throws DBusError, IOError; + + [DBus (name = "CancelDeviceCreation")] + public abstract void cancel_device_creation(string address) throws DBusError, IOError; + + [DBus (name = "RemoveDevice")] + public abstract void remove_device(GLib.ObjectPath device) throws DBusError, IOError; + + [DBus (name = "FindDevice")] + public abstract GLib.ObjectPath find_device(string address) throws DBusError, IOError; + + [DBus (name = "RegisterAgent")] + public abstract void register_agent(GLib.ObjectPath agent, string capability) throws DBusError, IOError; + + [DBus (name = "UnregisterAgent")] + public abstract void unregister_agent(GLib.ObjectPath agent) throws DBusError, IOError; + + [DBus (name = "PropertyChanged")] + public signal void property_changed(string name, GLib.Variant value); + + [DBus (name = "DeviceCreated")] + public signal void device_created(GLib.ObjectPath device); + + [DBus (name = "DeviceRemoved")] + public signal void device_removed(GLib.ObjectPath device); + + [DBus (name = "DeviceFound")] + public signal void device_found(string address, GLib.HashTable values); + + [DBus (name = "DeviceDisappeared")] + public signal void device_disappeared(string address); + } + + [DBus (name = "org.bluez.Adapter", timeout = 120000)] + public interface AdapterSync : GLib.Object { + + [DBus (name = "GetProperties")] + public abstract GLib.HashTable get_properties() throws DBusError, IOError; + + [DBus (name = "SetProperty")] + public abstract void set_property(string name, GLib.Variant value) throws DBusError, IOError; + + [DBus (name = "RequestSession")] + public abstract void request_session() throws DBusError, IOError; + + [DBus (name = "ReleaseSession")] + public abstract void release_session() throws DBusError, IOError; + + [DBus (name = "StartDiscovery")] + public abstract void start_discovery() throws DBusError, IOError; + + [DBus (name = "StopDiscovery")] + public abstract void stop_discovery() throws DBusError, IOError; + + [DBus (name = "ListDevices")] + public abstract GLib.ObjectPath[] list_devices() throws DBusError, IOError; + + [DBus (name = "CreateDevice")] + public abstract GLib.ObjectPath create_device(string address) throws DBusError, IOError; + + [DBus (name = "CreatePairedDevice")] + public abstract GLib.ObjectPath create_paired_device(string address, GLib.ObjectPath agent, string capability) throws DBusError, IOError; + + [DBus (name = "CancelDeviceCreation")] + public abstract void cancel_device_creation(string address) throws DBusError, IOError; + + [DBus (name = "RemoveDevice")] + public abstract void remove_device(GLib.ObjectPath device) throws DBusError, IOError; + + [DBus (name = "FindDevice")] + public abstract GLib.ObjectPath find_device(string address) throws DBusError, IOError; + + [DBus (name = "RegisterAgent")] + public abstract void register_agent(GLib.ObjectPath agent, string capability) throws DBusError, IOError; + + [DBus (name = "UnregisterAgent")] + public abstract void unregister_agent(GLib.ObjectPath agent) throws DBusError, IOError; + + [DBus (name = "PropertyChanged")] + public signal void property_changed(string name, GLib.Variant value); + + [DBus (name = "DeviceCreated")] + public signal void device_created(GLib.ObjectPath device); + + [DBus (name = "DeviceRemoved")] + public signal void device_removed(GLib.ObjectPath device); + + [DBus (name = "DeviceFound")] + public signal void device_found(string address, GLib.HashTable values); + + [DBus (name = "DeviceDisappeared")] + public signal void device_disappeared(string address); + } + + [DBus (name = "org.bluez.Agent", timeout = 120000)] + public interface Agent : GLib.Object { + + [DBus (name = "Authorize")] + public abstract void authorize(GLib.ObjectPath device, string uuid) throws DBusError, IOError; + + [DBus (name = "RequestPinCode")] + public abstract string request_pin_code(GLib.ObjectPath device) throws DBusError, IOError; + + [DBus (name = "DisplayPasskey")] + public abstract void display_passkey(GLib.ObjectPath device, uint passkey) throws DBusError, IOError; + + [DBus (name = "Release")] + public abstract void release() throws DBusError, IOError; + + [DBus (name = "Cancel")] + public abstract void cancel() throws DBusError, IOError; + + [DBus (name = "RequestConfirmation")] + public abstract void request_confirmation(GLib.ObjectPath device, uint passkey) throws DBusError, IOError; + + [DBus (name = "ConfirmModeChange")] + public abstract void confirm_mode_change(string mode) throws DBusError, IOError; + + [DBus (name = "RequestPasskey")] + public abstract uint request_passkey(GLib.ObjectPath device) throws DBusError, IOError; + } + + [DBus (name = "org.bluez.Agent", timeout = 120000)] + public interface AgentSync : GLib.Object { + + [DBus (name = "Authorize")] + public abstract void authorize(GLib.ObjectPath device, string uuid) throws DBusError, IOError; + + [DBus (name = "RequestPinCode")] + public abstract string request_pin_code(GLib.ObjectPath device) throws DBusError, IOError; + + [DBus (name = "DisplayPasskey")] + public abstract void display_passkey(GLib.ObjectPath device, uint passkey) throws DBusError, IOError; + + [DBus (name = "Release")] + public abstract void release() throws DBusError, IOError; + + [DBus (name = "Cancel")] + public abstract void cancel() throws DBusError, IOError; + + [DBus (name = "RequestConfirmation")] + public abstract void request_confirmation(GLib.ObjectPath device, uint passkey) throws DBusError, IOError; + + [DBus (name = "ConfirmModeChange")] + public abstract void confirm_mode_change(string mode) throws DBusError, IOError; + + [DBus (name = "RequestPasskey")] + public abstract uint request_passkey(GLib.ObjectPath device) throws DBusError, IOError; + } + + [DBus (name = "org.bluez.Device", timeout = 120000)] + public interface Device : GLib.Object { + + [DBus (name = "GetProperties")] + public abstract GLib.HashTable get_properties() throws DBusError, IOError; + + [DBus (name = "SetProperty")] + public abstract void set_property(string name, GLib.Variant value) throws DBusError, IOError; + + [DBus (name = "DiscoverServices")] + public abstract GLib.HashTable discover_services(string pattern) throws DBusError, IOError; + + [DBus (name = "CancelDiscovery")] + public abstract void cancel_discovery() throws DBusError, IOError; + + [DBus (name = "Disconnect")] + public abstract void disconnect() throws DBusError, IOError; + + [DBus (name = "PropertyChanged")] + public signal void property_changed(string name, GLib.Variant value); + + [DBus (name = "DisconnectRequested")] + public signal void disconnect_requested(); + } + + [DBus (name = "org.bluez.Device", timeout = 120000)] + public interface DeviceSync : GLib.Object { + + [DBus (name = "GetProperties")] + public abstract GLib.HashTable get_properties() throws DBusError, IOError; + + [DBus (name = "SetProperty")] + public abstract void set_property(string name, GLib.Variant value) throws DBusError, IOError; + + [DBus (name = "DiscoverServices")] + public abstract GLib.HashTable discover_services(string pattern) throws DBusError, IOError; + + [DBus (name = "CancelDiscovery")] + public abstract void cancel_discovery() throws DBusError, IOError; + + [DBus (name = "Disconnect")] + public abstract void disconnect() throws DBusError, IOError; + + [DBus (name = "PropertyChanged")] + public signal void property_changed(string name, GLib.Variant value); + + [DBus (name = "DisconnectRequested")] + public signal void disconnect_requested(); + } + } +} diff --git a/src/phone.vala b/src/phone.vala index 2abcf11..a2a9917 100644 --- a/src/phone.vala +++ b/src/phone.vala @@ -29,7 +29,7 @@ class PhoneMenu: BluetoothMenu group.insert (actions[i]); } - public PhoneMenu () + public PhoneMenu (Bluetooth bluetooth) { base ("phone"); diff --git a/src/service.vala b/src/service.vala index b6ae0f3..0f3d779 100644 --- a/src/service.vala +++ b/src/service.vala @@ -28,24 +28,27 @@ public class BluetoothIndicator private List device_items; private Dbusmenu.Menuitem menu; - public BluetoothIndicator () throws Error + public BluetoothIndicator (Bluetooth bluetooth) { - var phone = new PhoneMenu (); - var desktop = new DesktopMenu (); + var phone = new PhoneMenu (bluetooth); + var desktop = new DesktopMenu (bluetooth); - this.menus = new HashTable (str_hash, str_equal); - this.menus.insert ("phone", phone); - this.menus.insert ("desktop", desktop); + this.menus = new HashTable (str_hash, str_equal); + this.menus.insert ("phone", phone); + this.menus.insert ("desktop", desktop); - this.actions = new SimpleActionGroup (); - phone.add_actions_to_group (this.actions); - desktop.add_actions_to_group (this.actions); + this.actions = new SimpleActionGroup (); + phone.add_actions_to_group (this.actions); + desktop.add_actions_to_group (this.actions); } private void init_for_bus (DBusConnection bus) { this.bus = bus; + /// + /// + indicator_service = new Indicator.Service ("com.canonical.indicator.bluetooth.old"); menu_server = new Dbusmenu.Server ("/com/canonical/indicator/bluetooth/menu"); -- cgit v1.2.3 From 5bc8ff5460d1b596649de5de71790aec952430b4 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 1 Aug 2013 18:33:44 -0500 Subject: improve documentation on Killswitch, Bluetooth, and Bluez --- src/bluetooth.vala | 28 +++++++++++++--------------- src/bluez.vala | 7 +++++-- src/killswitch.vala | 23 +++++++++++++++-------- src/main.vala | 2 +- 4 files changed, 34 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/bluetooth.vala b/src/bluetooth.vala index ad5ee09..b6d0e55 100644 --- a/src/bluetooth.vala +++ b/src/bluetooth.vala @@ -17,37 +17,35 @@ * Charles Kerr */ +/** + * Base class for the bluetooth backend. + */ public class Bluetooth: Object { - /*** - **** Properties - ***/ - + /* whether or not our system can be seen by other bluetooth devices */ public bool discoverable { get; protected set; default = false; } public virtual void try_set_discoverable (bool b) {} + /* whether or not there are any bluetooth adapters powered up on the system */ public bool powered { get; protected set; default = false; } + /* whether or not bluetooth's been disabled, + either by a software setting or physical hardware switch */ public bool blocked { get; protected set; default = true; } public virtual void try_set_blocked (bool b) { - kill_switch.try_set_blocked (b); + killswitch.try_set_blocked (b); } /*** **** Killswitch Implementation ***/ - protected KillSwitch kill_switch; + private KillSwitch killswitch; - public Bluetooth (KillSwitch kill_switch) + public Bluetooth (KillSwitch killswitch) { - this.kill_switch = kill_switch; - - message ("changing blocked to %d", (int)!this.kill_switch.blocked); - blocked = this.kill_switch.blocked; - kill_switch.notify["blocked"].connect (() => { - message ("bluetooth changing blocked to %d", (int)kill_switch.blocked); - this.blocked = kill_switch.blocked; - }); + this.killswitch = killswitch; + blocked = killswitch.blocked; + killswitch.notify["blocked"].connect (() => blocked = killswitch.blocked ); } } diff --git a/src/bluez.vala b/src/bluez.vala index 178cc57..0f793cc 100644 --- a/src/bluez.vala +++ b/src/bluez.vala @@ -17,16 +17,19 @@ * Charles Kerr */ +/** + * Bluetooth implementaion which uses bluez over dbus + */ public class Bluez: Bluetooth { private org.bluez.Manager manager; private org.bluez.Adapter default_adapter; - public Bluez (KillSwitch kill_switch) + public Bluez (KillSwitch killswitch) { string default_adapter_object_path = null; - base (kill_switch); + base (killswitch); try { diff --git a/src/killswitch.vala b/src/killswitch.vala index 92b1a3c..cf5c6c8 100644 --- a/src/killswitch.vala +++ b/src/killswitch.vala @@ -20,13 +20,24 @@ /** * Monitors whether or not bluetooth is blocked, * either by software (e.g., a session configuration setting) - * or by hardware (e.g., user disabled it via a physical switch on her laptop) + * or by hardware (e.g., user disabled it via a physical switch on her laptop). + * + * The Bluetooth class uses this as a backend for its 'blocked' property. + * Other code can't even see this, so use Bluetooth.blocked instead. :) */ public class KillSwitch: Object { public bool blocked { get; protected set; default = false; } - public void try_set_blocked (bool blocked) + public virtual void try_set_blocked (bool blocked) {} +} + +/** + * On Linux systems, monitors /dev/rfkill to watch for bluetooth blockage + */ +public class RfKillSwitch: KillSwitch +{ + public override void try_set_blocked (bool blocked) { return_if_fail (this.blocked != blocked); @@ -42,10 +53,6 @@ public class KillSwitch: Object warning ("Could not write rfkill event: %s", strerror(errno)); } - /*** - **** Past this point, it's all RfKill implementation details... - ***/ - private class Entry { public uint32 idx; @@ -68,13 +75,13 @@ public class KillSwitch: Object return false; } - ~KillSwitch () + ~RfKillSwitch () { Source.remove (watch); Posix.close (fd); } - public KillSwitch () + public RfKillSwitch () { entries = new HashTable(direct_hash, direct_equal); diff --git a/src/main.vala b/src/main.vala index 7af617d..89c583b 100644 --- a/src/main.vala +++ b/src/main.vala @@ -7,7 +7,7 @@ main (string[] args) Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.GNOMELOCALEDIR); Intl.textdomain (Config.GETTEXT_PACKAGE); - var bluetooth = new Bluez (new KillSwitch ()); + var bluetooth = new Bluez (new RfKillSwitch ()); var service = new BluetoothIndicator (bluetooth); service.run (); -- cgit v1.2.3 From dfd38e506145b8569611e5fb0eb8cb49eef1b7b2 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 1 Aug 2013 20:30:08 -0500 Subject: move indicator3-0.4.vapi to the vapi dir. it'll go away soon enough... --- src/Makefile.am | 4 +- src/indicator3-0.4.vapi | 145 ------------------------------------------------ 2 files changed, 2 insertions(+), 147 deletions(-) delete mode 100644 src/indicator3-0.4.vapi (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 720c981..f685a97 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,6 @@ pkglibexec_PROGRAMS = indicator-bluetooth-service -indicator_bluetooth_service_SOURCES = \ - indicator3-0.4.vapi \ +indicator_bluetooth_service_SOURCES = \ org-bluez.vala \ bluetooth.vala \ bluez.vala \ @@ -16,6 +15,7 @@ indicator_bluetooth_service_VALAFLAGS = \ --ccode \ --vapidir=$(top_srcdir)/vapi/ \ --vapidir=./ \ + --pkg indicator3-0.4 \ --pkg gnome-bluetooth-1.0 \ --pkg config \ --pkg rfkill \ diff --git a/src/indicator3-0.4.vapi b/src/indicator3-0.4.vapi deleted file mode 100644 index 44204cd..0000000 --- a/src/indicator3-0.4.vapi +++ /dev/null @@ -1,145 +0,0 @@ -/* indicator-0.4.vapi generated by vapigen, do not modify. */ - -namespace Indicator { - [CCode (cheader_filename = "libindicator/indicator-desktop-shortcuts.h", type_check_function = "INDICATOR_IS_DESKTOP_SHORTCUTS", type_id = "indicator_desktop_shortcuts_get_type")] - public class DesktopShortcuts : GLib.Object { - [CCode (has_construct_function = false)] - public DesktopShortcuts (string file, string identity); - public unowned string get_nicks (); - public bool nick_exec (string nick); - public unowned string nick_get_name (string nick); - public string desktop_file { construct; } - [NoAccessorMethod] - public string identity { owned get; construct; } - } - [CCode (cheader_filename = "libindicator/indicator-object.h", type_check_function = "INDICATOR_IS_OBJECT", type_id = "indicator_object_get_type ()")] - public class Object : GLib.Object { - [CCode (has_construct_function = false)] - public Object (); - public bool check_environment (string env); - [NoWrapper] - public virtual void entry_activate (Indicator.ObjectEntry entry, uint timestamp); - [NoWrapper] - public virtual void entry_activate_window (Indicator.ObjectEntry entry, uint windowid, uint timestamp); - [NoWrapper] - public virtual void entry_being_removed (Indicator.ObjectEntry entry); - [NoWrapper] - public virtual void entry_close (Indicator.ObjectEntry entry, uint timestamp); - [NoWrapper] - public virtual void entry_was_added (Indicator.ObjectEntry entry); - [CCode (has_construct_function = false)] - public Object.from_file (string file); - [NoWrapper] - public virtual signal unowned string get_accessible_desc (); - public virtual GLib.List get_entries (); - public unowned string[] get_environment (); - [NoWrapper] - public virtual unowned Gtk.Image get_image (); - [NoWrapper] - public virtual unowned Gtk.Label get_label (); - public virtual uint get_location (Indicator.ObjectEntry entry); - [NoWrapper] - public virtual unowned Gtk.Menu get_menu (); - [NoWrapper] - public virtual unowned string get_name_hint (); - public virtual bool get_show_now (Indicator.ObjectEntry entry); - public void set_environment (string[] env); - public void set_visible (bool visible); - [NoAccessorMethod] - public bool indicator_object_default_visibility { get; set; } - public virtual signal void accessible_desc_update (Indicator.ObjectEntry entry); - public virtual signal void entry_added (Indicator.ObjectEntry entry); - public virtual signal void entry_moved (Indicator.ObjectEntry entry, uint old_pos, uint new_pos); - public virtual signal void entry_removed (Indicator.ObjectEntry entry); - public virtual signal void entry_scrolled (Indicator.ObjectEntry entry, uint delta, Indicator.ScrollDirection direction); - public virtual signal void menu_show (Indicator.ObjectEntry entry, uint timestamp); - public virtual signal void secondary_activate (Indicator.ObjectEntry entry, uint timestamp); - public virtual signal void show_now_changed (Indicator.ObjectEntry entry, bool show_now_state); - } - [CCode (cheader_filename = "libindicator/indicator-object.h")] - [Compact] - public class ObjectEntry { - public weak string accessible_desc; - public weak Gtk.Image image; - public weak Gtk.Label label; - public weak Gtk.Menu menu; - public weak string name_hint; - public weak Indicator.Object parent_object; - public static void activate (Indicator.Object io, Indicator.ObjectEntry entry, uint timestamp); - public static void activate_window (Indicator.Object io, Indicator.ObjectEntry entry, uint windowid, uint timestamp); - public static void close (Indicator.Object io, Indicator.ObjectEntry entry, uint timestamp); - } - [CCode (cheader_filename = "libindicator/indicator-service.h", type_check_function = "INDICATOR_IS_SERVICE", type_id = "indicator_service_get_type")] - public class Service : GLib.Object { - [CCode (has_construct_function = false)] - public Service (string name); - [CCode (cname = "indicator_service_new_version", has_construct_function = false)] - public Service.with_version (string name, uint version); - [NoAccessorMethod] - public string name { owned get; set; } - [NoAccessorMethod] - public uint version { get; set; } - public virtual signal void shutdown (); - } - [CCode (cheader_filename = "libindicator/indicator-service-manager.h", type_check_function = "INDICATOR_IS_SERVICE_MANAGER", type_id = "indicator_service_manager_get_type")] - public class ServiceManager : GLib.Object { - [CCode (has_construct_function = false)] - public ServiceManager (string dbus_name); - public bool connected (); - public void set_refresh (uint time_in_ms); - [CCode (cname = "indicator_service_manager_new_version", has_construct_function = false)] - public ServiceManager.with_version (string dbus_name, uint version); - [NoAccessorMethod] - public string name { owned get; set; } - [NoAccessorMethod] - public uint version { get; set; } - public virtual signal void connection_change (bool connected); - } - [CCode (cheader_filename = "libindicator/indicator-object.h", cprefix = "INDICATOR_OBJECT_SCROLL_", has_type_id = false)] - public enum ScrollDirection { - UP, - DOWN, - LEFT, - RIGHT - } - [CCode (cheader_filename = "libindicator/indicator.h", has_target = false)] - public delegate GLib.Type get_type_t (); - [CCode (cheader_filename = "libindicator/indicator.h", has_target = false)] - public delegate unowned string get_version_t (); - [CCode (cheader_filename = "libindicator/indicator.h")] - public const string GET_TYPE_S; - [CCode (cheader_filename = "libindicator/indicator.h")] - public const string GET_VERSION_S; - [CCode (cheader_filename = "libindicator/indicator-gobject.h")] - public const string OBJECT_DEFAULT_VISIBILITY; - [CCode (cheader_filename = "libindicator/indicator-gobject.h")] - public const string OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE; - [CCode (cheader_filename = "libindicator/indicator-gobject.h")] - public const string OBJECT_SIGNAL_ENTRY_ADDED; - [CCode (cheader_filename = "libindicator/indicator-gobject.h")] - public const string OBJECT_SIGNAL_ENTRY_MOVED; - [CCode (cheader_filename = "libindicator/indicator-gobject.h")] - public const string OBJECT_SIGNAL_ENTRY_REMOVED; - [CCode (cheader_filename = "libindicator/indicator-gobject.h")] - public const string OBJECT_SIGNAL_ENTRY_SCROLLED; - [CCode (cheader_filename = "libindicator/indicator-gobject.h")] - public const string OBJECT_SIGNAL_MENU_SHOW; - [CCode (cheader_filename = "libindicator/indicator-gobject.h")] - public const string OBJECT_SIGNAL_SECONDARY_ACTIVATE; - [CCode (cheader_filename = "libindicator/indicator-gobject.h")] - public const string OBJECT_SIGNAL_SHOW_NOW_CHANGED; - [CCode (cheader_filename = "libindicator/indicator-service-manager.h")] - public const string SERVICE_MANAGER_SIGNAL_CONNECTION_CHANGE; - [CCode (cheader_filename = "libindicator/indicator-service.h")] - public const string SERVICE_SIGNAL_SHUTDOWN; - [CCode (cheader_filename = "libindicator/indicator.h")] - public const int SET_VERSION; - [CCode (cheader_filename = "libindicator/indicator.h")] - public const string VERSION; - [CCode (cheader_filename = "libindicator/indicator.h", cname = "get_version")] - public static unowned string get_version (); - [CCode (cheader_filename = "libindicator/indicator-image-helper.h")] - public static unowned Gtk.Image image_helper (string name); - [CCode (cheader_filename = "libindicator/indicator-image-helper.h")] - public static void image_helper_update (Gtk.Image image, string name); -} \ No newline at end of file -- cgit v1.2.3 From 43059ed45ac41e1bf6172b3eec236ea086d81cda Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Sat, 3 Aug 2013 10:47:23 -0500 Subject: change the 'BluetoothMenu' superclass to 'Profile' --- src/Makefile.am | 2 +- src/desktop.vala | 34 ++++++++++++++++------------------ src/menu.vala | 53 ----------------------------------------------------- src/phone.vala | 14 ++++++-------- src/profile.vala | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/service.vala | 20 +++++++++----------- 6 files changed, 86 insertions(+), 91 deletions(-) delete mode 100644 src/menu.vala create mode 100644 src/profile.vala (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index f685a97..b5aa8f1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,8 +6,8 @@ indicator_bluetooth_service_SOURCES = \ bluez.vala \ desktop.vala \ main.vala \ - menu.vala \ phone.vala \ + profile.vala \ killswitch.vala \ service.vala diff --git a/src/desktop.vala b/src/desktop.vala index 351770a..a7b8995 100644 --- a/src/desktop.vala +++ b/src/desktop.vala @@ -17,7 +17,7 @@ * Charles Kerr */ -class DesktopMenu: BluetoothMenu +class Desktop: Profile { private Settings settings; private Bluetooth bluetooth; @@ -27,31 +27,29 @@ class DesktopMenu: BluetoothMenu public override void add_actions_to_group (SimpleActionGroup group) { - base.add_actions_to_group (group); - - for (var i=0; i this.update_root_action_state()); - settings.changed["visible"].connect (()=> this.update_root_action_state()); + bluetooth.notify.connect (() => update_root_action_state()); + settings.changed["visible"].connect (()=> update_root_action_state()); Menu section; MenuItem item; @@ -63,12 +61,12 @@ class DesktopMenu: BluetoothMenu item = new MenuItem ("Visible", "indicator.desktop-discoverable"); item.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.switch"); section.append_item (item); - this.menu.append_section (null, section); + menu.append_section (null, section); section = new Menu (); section.append (_("Set Up New Device…"), "indicator.desktop-wizard"); section.append (_("Bluetooth Settings…"), "indicator.desktop-settings"); - this.menu.append_section (null, section); + menu.append_section (null, section); } Action create_enabled_action (Bluetooth bluetooth) @@ -119,7 +117,7 @@ class DesktopMenu: BluetoothMenu bool blocked = bluetooth.blocked; bool powered = bluetooth.powered; - settings.changed["visible"].connect (()=> this.update_root_action_state()); + settings.changed["visible"].connect (()=> update_root_action_state()); bool visible = powered && settings.get_boolean("visible"); diff --git a/src/menu.vala b/src/menu.vala deleted file mode 100644 index a2c5449..0000000 --- a/src/menu.vala +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - * - * Authors: - * Charles Kerr - */ - -class BluetoothMenu: Object -{ - protected Menu root; - protected Menu menu; - - public virtual void add_actions_to_group (SimpleActionGroup group) - { - } - - public BluetoothMenu (string profile) - { - this.menu = new Menu (); - - var root_item = new MenuItem (null, "indicator.root-" + profile); - root_item.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.root"); - root_item.set_submenu (this.menu); - - this.root = new Menu (); - this.root.append_item (root_item); - } - - public void export (DBusConnection connection, string object_path) - { - try - { - message ("exporting on %s", object_path); - connection.export_menu_model (object_path, this.root); - } - catch (Error e) - { - critical ("%s", e.message); - } - } -} diff --git a/src/phone.vala b/src/phone.vala index a2a9917..8c9c816 100644 --- a/src/phone.vala +++ b/src/phone.vala @@ -17,29 +17,27 @@ * Charles Kerr */ -class PhoneMenu: BluetoothMenu +class Phone: Profile { private Action[] actions; public override void add_actions_to_group (SimpleActionGroup group) { - base.add_actions_to_group (group); - for (var i=0; i. + * + * Authors: + * Charles Kerr + */ + +class Profile: Object +{ + protected string name; + protected Menu root; + protected Menu menu; + + public virtual void add_actions_to_group (SimpleActionGroup group) {} + + public Profile (string name) + { + this.name = name; + + menu = new Menu (); + + var root_item = new MenuItem (null, "indicator.root-" + name); + root_item.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.root"); + root_item.set_submenu (menu); + + root = new Menu (); + root.append_item (root_item); + } + + public void export_menu (DBusConnection connection, string object_path) + { + try + { + message ("exporting '%s' on %s", name, object_path); + connection.export_menu_model (object_path, this.root); + } + catch (Error e) + { + critical ("%s", e.message); + } + } +} diff --git a/src/service.vala b/src/service.vala index 0f3d779..10f7f25 100644 --- a/src/service.vala +++ b/src/service.vala @@ -12,7 +12,7 @@ public class BluetoothIndicator { private MainLoop loop; private SimpleActionGroup actions; - private HashTable menus; + private HashTable profiles; private DBusConnection bus; private Indicator.Service indicator_service; @@ -30,16 +30,13 @@ public class BluetoothIndicator public BluetoothIndicator (Bluetooth bluetooth) { - var phone = new PhoneMenu (bluetooth); - var desktop = new DesktopMenu (bluetooth); + profiles = new HashTable (str_hash, str_equal); + profiles.insert ("phone", new Phone (bluetooth)); + profiles.insert ("desktop", new Desktop (bluetooth)); - this.menus = new HashTable (str_hash, str_equal); - this.menus.insert ("phone", phone); - this.menus.insert ("desktop", desktop); - - this.actions = new SimpleActionGroup (); - phone.add_actions_to_group (this.actions); - desktop.add_actions_to_group (this.actions); + actions = new SimpleActionGroup (); + foreach (Profile profile in profiles.get_values()) + profile.add_actions_to_group (actions); } private void init_for_bus (DBusConnection bus) @@ -178,7 +175,7 @@ public class BluetoothIndicator critical ("%s", e.message); } - this.menus.@foreach ( (profile, menu) => menu.export (connection, @"/com/canonical/indicator/bluetooth/$profile")); + this.profiles.@foreach ((name,profile) => profile.export_menu (connection, @"/com/canonical/indicator/bluetooth/$name")); } void on_name_lost (DBusConnection connection, string name) @@ -358,6 +355,7 @@ private class BluetoothMenuItem : Dbusmenu.Menuitem { for (var i = 0; uuids[i] != null; i++) { +message ("alias %s uuid #%d: %s", alias, i, uuids[i]); if (uuids[i] == "OBEXObjectPush") can_send = true; if (uuids[i] == "OBEXFileTransfer") -- cgit v1.2.3 From d73ae6424c53b2b8760b81d24a71cae6a3169a59 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Sat, 3 Aug 2013 10:47:35 -0500 Subject: tweak startup comments --- src/main.vala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/main.vala b/src/main.vala index 89c583b..b88e94e 100644 --- a/src/main.vala +++ b/src/main.vala @@ -2,14 +2,17 @@ public static int main (string[] args) { + // set up i18n Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8"); Intl.setlocale (LocaleCategory.ALL, ""); Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.GNOMELOCALEDIR); Intl.textdomain (Config.GETTEXT_PACKAGE); + // create the backend var bluetooth = new Bluez (new RfKillSwitch ()); - var service = new BluetoothIndicator (bluetooth); + // start the service + var service = new BluetoothIndicator (bluetooth); service.run (); return Posix.EXIT_SUCCESS; -- cgit v1.2.3 From 9837ce82575d089b52269ff600820d565be3fd22 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Sat, 3 Aug 2013 10:48:02 -0500 Subject: initial support for individual devices in Bluetooth class --- src/bluetooth.vala | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/bluez.vala | 52 ++++++++++++++++++-- 2 files changed, 185 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/bluetooth.vala b/src/bluetooth.vala index b6d0e55..1952d1c 100644 --- a/src/bluetooth.vala +++ b/src/bluetooth.vala @@ -36,6 +36,144 @@ public class Bluetooth: Object killswitch.try_set_blocked (b); } + public class Device: Object { + public string name { get; construct; } + public bool supports_browsing { get; construct; } + public bool supports_file_transfer { get; construct; } + public Device (string name, + bool supports_browsing, + bool supports_file_transfer) { + Object (name: name, + supports_browsing: supports_browsing, + supports_file_transfer: supports_file_transfer); + } + } + + private static uint16 get_uuid16_from_uuid (string uuid) + { + uint16 uuid16; + + string[] tokens = uuid.split ("-", 1); + if (tokens.length > 0) + uuid16 = (uint16) uint64.parse ("0x"+tokens[0]); + else + uuid16 = 0; + + return uuid16; + } + + protected static bool uuid_supports_file_transfer (string uuid) + { + return get_uuid16_from_uuid (uuid) == 0x1105; // OBEXObjectPush + } + + protected static bool uuid_supports_browsing (string uuid) + { + return get_uuid16_from_uuid (uuid) == 0x1106; // OBEXFileTransfer + } + + public enum DeviceType + { + COMPUTER, + PHONE, + MODEM, + NETWORK, + HEADSET, + HEADPHONES, + VIDEO, + OTHER_AUDIO, + JOYPAD, + KEYPAD, + KEYBOARD, + TABLET, + MOUSE, + PRINTER, + CAMERA + } + + protected static DeviceType class_to_device_type (uint32 c) + { + switch ((c & 0x1f00) >> 8) + { + case 0x01: + return DeviceType.COMPUTER; + + case 0x02: + switch ((c & 0xfc) >> 2) + { + case 0x01: + case 0x02: + case 0x03: + case 0x05: + return DeviceType.PHONE; + + case 0x04: + return DeviceType.MODEM; + } + break; + + case 0x03: + return DeviceType.NETWORK; + + case 0x04: + switch ((c & 0xfc) >> 2) + { + case 0x01: + case 0x02: + return DeviceType.HEADSET; + + case 0x06: + return DeviceType.HEADPHONES; + + case 0x0b: // vcr + case 0x0c: // video camera + case 0x0d: // camcorder + return DeviceType.VIDEO; + + default: + return DeviceType.OTHER_AUDIO; + } + //break; + + case 0x05: + switch ((c & 0xc0) >> 6) + { + case 0x00: + switch ((c & 0x1e) >> 2) + { + case 0x01: + case 0x02: + return DeviceType.JOYPAD; + } + break; + + case 0x01: + return DeviceType.KEYBOARD; + + case 0x02: + switch ((c & 0x1e) >> 2) + { + case 0x05: + return DeviceType.TABLET; + + default: + return DeviceType.MOUSE; + } + } + break; + + case 0x06: + if ((c & 0x80) != 0) + return DeviceType.PRINTER; + if ((c & 0x20) != 0) + return DeviceType.CAMERA; + break; + } + + return 0; + } + + /*** **** Killswitch Implementation ***/ diff --git a/src/bluez.vala b/src/bluez.vala index 0f793cc..b0c8761 100644 --- a/src/bluez.vala +++ b/src/bluez.vala @@ -24,12 +24,15 @@ public class Bluez: Bluetooth { private org.bluez.Manager manager; private org.bluez.Adapter default_adapter; + private HashTable devices; public Bluez (KillSwitch killswitch) { + base (killswitch); + string default_adapter_object_path = null; - base (killswitch); + this.devices = new HashTable(str_hash, str_equal); try { @@ -51,17 +54,56 @@ public class Bluez: Bluetooth if (object_path != null) try { message ("using default adapter at %s", object_path); - this.default_adapter = Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", object_path); - this.default_adapter.property_changed.connect(() => this.on_default_adapter_properties_changed()); + default_adapter = Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", object_path); + default_adapter.property_changed.connect(() => on_default_adapter_properties_changed()); + + default_adapter.device_created.connect((adapter, path) => add_device (path)); + default_adapter.device_removed.connect((adapter, path) => devices.remove (path)); + foreach (string device_path in default_adapter.list_devices()) + add_device (device_path); } catch (Error e) - { + { critical ("%s", e.message); - } + } this.on_default_adapter_properties_changed (); } + private void add_device (string object_path) + { + try + { + org.bluez.Device device = Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", object_path); + message ("got device proxy for %s", object_path); + var properties = device.get_properties (); + + Variant v = properties.lookup ("Alias"); + if (v == null) + v = properties.lookup ("Name"); + string name = v == null ? _("Unknown") : v.get_string(); + + bool supports_browsing = false; + v = properties.lookup ("UUIDs"); + message ("%s", v.print(true)); + + bool supports_file_transfer = false; + + //protected static bool uuid_supports_file_transfer (string uuid) + //protected static bool uuid_supports_browsing (string uuid) + + var dev = new Bluetooth.Device (name, + supports_browsing, + supports_file_transfer); + devices.insert (object_path, dev); + message ("devices.size() is %u", devices.size()); + } + catch (Error e) + { + critical ("%s", e.message); + } + } + private void on_default_adapter_properties_changed () { bool is_discoverable = false; -- cgit v1.2.3 From c1ce02f2b8cd198712606888d08b36f1e7aefe39 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 5 Aug 2013 16:48:17 -0500 Subject: fully implement the bluez/device backend. in the desktop profile, add menuitems for the devices. --- src/Makefile.am | 8 +- src/bluetooth.vala | 191 +++++------------------- src/bluez.vala | 288 ++++++++++++++++++++++++++++++----- src/desktop.vala | 192 ++++++++++++++++++++++-- src/killswitch.vala | 7 +- src/main.vala | 2 +- src/phone.vala | 2 +- src/profile.vala | 4 +- src/service.vala | 421 ++-------------------------------------------------- 9 files changed, 489 insertions(+), 626 deletions(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index b5aa8f1..87f4231 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,6 +5,7 @@ indicator_bluetooth_service_SOURCES = \ bluetooth.vala \ bluez.vala \ desktop.vala \ + device.vala \ main.vala \ phone.vala \ profile.vala \ @@ -15,18 +16,17 @@ indicator_bluetooth_service_VALAFLAGS = \ --ccode \ --vapidir=$(top_srcdir)/vapi/ \ --vapidir=./ \ - --pkg indicator3-0.4 \ - --pkg gnome-bluetooth-1.0 \ --pkg config \ --pkg rfkill \ --pkg posix \ --pkg glib-2.0 \ - --pkg gtk+-3.0 \ - --pkg Dbusmenu-0.4 + --pkg gtk+-3.0 +# -w to disable warnings for vala-generated code indicator_bluetooth_service_CFLAGS = \ -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \ -DLOCALE_DIR=\"$(datadir)/locale\" \ + -w \ $(INDICATOR_BLUETOOTH_SERVICE_CFLAGS) indicator_bluetooth_service_LDADD = \ diff --git a/src/bluetooth.vala b/src/bluetooth.vala index 1952d1c..98464a4 100644 --- a/src/bluetooth.vala +++ b/src/bluetooth.vala @@ -17,173 +17,64 @@ * Charles Kerr */ + /** - * Base class for the bluetooth backend. + * Abstract interface for the Bluetooth backend. */ -public class Bluetooth: Object +public interface Bluetooth: Object { - /* whether or not our system can be seen by other bluetooth devices */ - public bool discoverable { get; protected set; default = false; } - public virtual void try_set_discoverable (bool b) {} + /* True if there are any bluetooth adapters powered up on the system. + In short, whether or not this system's bluetooth is "on". */ + public abstract bool powered { get; protected set; } - /* whether or not there are any bluetooth adapters powered up on the system */ - public bool powered { get; protected set; default = false; } + /* True if our system can be seen by other bluetooth devices */ + public abstract bool discoverable { get; protected set; } + public abstract void try_set_discoverable (bool discoverable); - /* whether or not bluetooth's been disabled, - either by a software setting or physical hardware switch */ - public bool blocked { get; protected set; default = true; } - public virtual void try_set_blocked (bool b) { - killswitch.try_set_blocked (b); - } + /* True if bluetooth's blocked. This can be soft-blocked by software and + * hard-blocked physically, eg by a laptop's network killswitch */ + public abstract bool blocked { get; protected set; } - public class Device: Object { - public string name { get; construct; } - public bool supports_browsing { get; construct; } - public bool supports_file_transfer { get; construct; } - public Device (string name, - bool supports_browsing, - bool supports_file_transfer) { - Object (name: name, - supports_browsing: supports_browsing, - supports_file_transfer: supports_file_transfer); - } - } - - private static uint16 get_uuid16_from_uuid (string uuid) - { - uint16 uuid16; - - string[] tokens = uuid.split ("-", 1); - if (tokens.length > 0) - uuid16 = (uint16) uint64.parse ("0x"+tokens[0]); - else - uuid16 = 0; - - return uuid16; - } - - protected static bool uuid_supports_file_transfer (string uuid) - { - return get_uuid16_from_uuid (uuid) == 0x1105; // OBEXObjectPush - } - - protected static bool uuid_supports_browsing (string uuid) - { - return get_uuid16_from_uuid (uuid) == 0x1106; // OBEXFileTransfer - } - - public enum DeviceType - { - COMPUTER, - PHONE, - MODEM, - NETWORK, - HEADSET, - HEADPHONES, - VIDEO, - OTHER_AUDIO, - JOYPAD, - KEYPAD, - KEYBOARD, - TABLET, - MOUSE, - PRINTER, - CAMERA - } - - protected static DeviceType class_to_device_type (uint32 c) - { - switch ((c & 0x1f00) >> 8) - { - case 0x01: - return DeviceType.COMPUTER; + /* Try to block/unblock bluetooth. This can fail if it's overridden + by the system, eg by a laptop's network killswitch */ + public abstract void try_set_blocked (bool b); - case 0x02: - switch ((c & 0xfc) >> 2) - { - case 0x01: - case 0x02: - case 0x03: - case 0x05: - return DeviceType.PHONE; + /* Get a list of the Device structs that we know about */ + public abstract List get_devices (); - case 0x04: - return DeviceType.MODEM; - } - break; + /* Emitted when one or more of the devices is added, removed, or changed */ + public signal void devices_changed (); - case 0x03: - return DeviceType.NETWORK; - - case 0x04: - switch ((c & 0xfc) >> 2) - { - case 0x01: - case 0x02: - return DeviceType.HEADSET; - - case 0x06: - return DeviceType.HEADPHONES; - - case 0x0b: // vcr - case 0x0c: // video camera - case 0x0d: // camcorder - return DeviceType.VIDEO; - - default: - return DeviceType.OTHER_AUDIO; - } - //break; - - case 0x05: - switch ((c & 0xc0) >> 6) - { - case 0x00: - switch ((c & 0x1e) >> 2) - { - case 0x01: - case 0x02: - return DeviceType.JOYPAD; - } - break; - - case 0x01: - return DeviceType.KEYBOARD; - - case 0x02: - switch ((c & 0x1e) >> 2) - { - case 0x05: - return DeviceType.TABLET; - - default: - return DeviceType.MOUSE; - } - } - break; - - case 0x06: - if ((c & 0x80) != 0) - return DeviceType.PRINTER; - if ((c & 0x20) != 0) - return DeviceType.CAMERA; - break; - } - - return 0; - } + /* Try to connect/disconnect a particular device. + The device_key argument comes from the Device struct */ + public abstract void set_device_connected (uint device_key, bool connected); +} - /*** - **** Killswitch Implementation - ***/ +/** + * Base class for Bluetooth objects that use a killswitch to implement + * the 'discoverable' property. + */ +public abstract class KillswitchBluetooth: Object, Bluetooth +{ private KillSwitch killswitch; - public Bluetooth (KillSwitch killswitch) + public KillswitchBluetooth (KillSwitch killswitch) { + // always sync our 'blocked' property with the one in killswitch this.killswitch = killswitch; blocked = killswitch.blocked; killswitch.notify["blocked"].connect (() => blocked = killswitch.blocked ); } + + public bool powered { get; protected set; default = false; } + public bool discoverable { get; protected set; default = false; } + public bool blocked { get; protected set; default = true; } + public void try_set_blocked (bool b) { killswitch.try_set_blocked (b); } + + // empty implementations + public abstract void try_set_discoverable (bool b); + public abstract List get_devices (); + public abstract void set_device_connected (uint device_key, bool connected); } diff --git a/src/bluez.vala b/src/bluez.vala index b0c8761..b4d7836 100644 --- a/src/bluez.vala +++ b/src/bluez.vala @@ -18,90 +18,295 @@ */ /** - * Bluetooth implementaion which uses bluez over dbus + * Bluetooth implementaion which uses org.bluez on DBus */ -public class Bluez: Bluetooth +public class Bluez: KillswitchBluetooth { private org.bluez.Manager manager; private org.bluez.Adapter default_adapter; - private HashTable devices; + private HashTable path_to_proxy; + private HashTable path_to_id; + private HashTable id_to_path; + private HashTable id_to_device; + private uint next_device_id = 1; public Bluez (KillSwitch killswitch) { base (killswitch); - string default_adapter_object_path = null; + string adapter_path = null; - this.devices = new HashTable(str_hash, str_equal); + id_to_path = new HashTable (direct_hash, direct_equal); + id_to_device = new HashTable (direct_hash, direct_equal); + path_to_id = new HashTable (str_hash, str_equal); + path_to_proxy = new HashTable (str_hash, str_equal); try { manager = Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", "/"); - manager.default_adapter_changed.connect ((object_path) => on_default_adapter_changed (object_path)); - default_adapter_object_path = manager.default_adapter (); + adapter_path = manager.default_adapter (); } catch (Error e) - { - critical ("%s", e.message); - } + { + critical (@"$(e.message)"); + } - on_default_adapter_changed (default_adapter_object_path); + on_default_adapter_changed (adapter_path); } private void on_default_adapter_changed (string? object_path) { if (object_path != null) try { - message ("using default adapter at %s", object_path); + message (@"using default adapter at $object_path"); default_adapter = Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", object_path); default_adapter.property_changed.connect(() => on_default_adapter_properties_changed()); + default_adapter.device_removed.connect((adapter, path) => { + var id = path_to_id.lookup (path); + path_to_id.remove (path); + id_to_path.remove (id); + id_to_device.remove (id); + devices_changed (); + }); + default_adapter.device_created.connect((adapter, path) => add_device (path)); - default_adapter.device_removed.connect((adapter, path) => devices.remove (path)); foreach (string device_path in default_adapter.list_devices()) add_device (device_path); } catch (Error e) { - critical ("%s", e.message); + critical (@"$(e.message)"); } this.on_default_adapter_properties_changed (); } - private void add_device (string object_path) + private static uint16 get_uuid16_from_uuid_string (string uuid) + { + uint16 uuid16; + + string[] tokens = uuid.split ("-", 1); + if (tokens.length > 0) + uuid16 = (uint16) uint64.parse ("0x"+tokens[0]); + else + uuid16 = 0; + + return uuid16; + } + + /* A device supports file transfer if OBEXObjectPush is in its uuid list */ + private bool device_supports_file_transfer (uint16[] uuids) + { + foreach (uint16 uuid16 in uuids) + if (uuid16 == 0x1105) // OBEXObjectPush + return true; + + return false; + } + + /* A device supports browsing if OBEXFileTransfer is in its uuid list */ + private bool device_supports_browsing (uint16[] uuids) { + foreach (uint16 uuid16 in uuids) + if (uuid16 == 0x1106) // OBEXFileTransfer + return true; + + return false; + } + + /* headsets, audio sinks, and input devices are connectable. + * + * TODO: this duplicates the behavior of the indicator from when it used + * gnome-bluetooth as a backend. Are there other interfaces we care about? */ + private DBusInterfaceInfo[] get_connectable_interfaces (DBusProxy device) + { + DBusInterfaceInfo[] connectable_interfaces = {}; + try { - org.bluez.Device device = Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", object_path); - message ("got device proxy for %s", object_path); - var properties = device.get_properties (); + var iname = "org.freedesktop.DBus.Introspectable.Introspect"; + var intro = device.call_sync (iname, null, DBusCallFlags.NONE, -1); - Variant v = properties.lookup ("Alias"); - if (v == null) - v = properties.lookup ("Name"); - string name = v == null ? _("Unknown") : v.get_string(); + if ((intro != null) && (intro.n_children() > 0)) + { + string xml = intro.get_child_value(0).get_string(); + var info = new DBusNodeInfo.for_xml (xml); + if (info != null) + { + foreach (DBusInterfaceInfo i in info.interfaces) + { + if ((i.name == "org.bluez.AudioSink") || + (i.name == "org.bluez.Headset") || + (i.name == "org.bluez.Input")) + { + connectable_interfaces += i; + } + } + } + } + } + catch (Error e) + { + critical (@"$(e.message)"); + } - bool supports_browsing = false; - v = properties.lookup ("UUIDs"); - message ("%s", v.print(true)); + return connectable_interfaces; + } - bool supports_file_transfer = false; + private bool device_is_connectable (DBusProxy device) + { + var connectable_interfaces = get_connectable_interfaces (device); + return connectable_interfaces.length > 0; + } - //protected static bool uuid_supports_file_transfer (string uuid) - //protected static bool uuid_supports_browsing (string uuid) + private void device_connect (DBusProxy proxy) + { + var connection = proxy.get_connection (); + var object_path = proxy.get_object_path (); - var dev = new Bluetooth.Device (name, - supports_browsing, - supports_file_transfer); - devices.insert (object_path, dev); - message ("devices.size() is %u", devices.size()); + foreach (var i in get_connectable_interfaces (proxy)) + { + try + { + debug (@"trying to connect to $object_path: $(i.name)"); + connection.call_sync ("org.bluez", + object_path, + i.name, + "Connect", + null, + null, + DBusCallFlags.NONE, + -1); + } + catch (Error e) + { + debug (@"Unable to call $(i.name).Connect() on $(proxy.get_object_path()): $(e.message)"); + } + } + } + + private void add_device (string object_path) + { + if (!path_to_proxy.contains (object_path)) + { + try + { + org.bluez.Device device = Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", object_path); + path_to_proxy.insert (object_path, device); + device.property_changed.connect(() => update_device (device)); + update_device (device); + } + catch (Error e) + { + critical (@"$(e.message)"); + } + } + } + + private void update_device (org.bluez.Device device_proxy) + { + HashTable properties; + + try { + properties = device_proxy.get_properties (); + } catch (Error e) { + critical (@"$(e.message)"); + return; + } + + // look up our id for this device. + // if we don't have one yet, create one. + var object_path = (device_proxy as DBusProxy).get_object_path(); + var id = path_to_id.lookup (object_path); + if (id == 0) + { + id = next_device_id ++; + id_to_path.insert (id, object_path); + path_to_id.insert (object_path, id); + } + + // look up the device's type + Device.Type type; + var v = properties.lookup ("Class"); + if (v == null) + type = Device.Type.OTHER; + else + type = Device.class_to_device_type (v.get_uint32()); + + // look up the device's human-readable name + v = properties.lookup ("Alias"); + if (v == null) + v = properties.lookup ("Name"); + string name = v == null ? _("Unknown") : v.get_string (); + + // look up the device's bus address + v = properties.lookup ("Address"); + string address = v.get_string (); + + // look up the device's bus address + Icon icon; + v = properties.lookup ("Icon"); + if (v == null) + icon = new ThemedIcon ("unknown"); + else + icon = new ThemedIcon (v.get_string()); + + // derive a Connectable flag for this device + var is_connectable = device_is_connectable (device_proxy as DBusProxy); + + // look up the device's Connected flag + v = properties.lookup ("Connected"); + bool is_connected = (v != null) && v.get_boolean (); + + // derive the uuid-related attributes we care about + v = properties.lookup ("UUIDs"); + string[] uuid_strings = v.dup_strv (); + uint16[] uuids = {}; + foreach (string s in uuid_strings) + uuids += get_uuid16_from_uuid_string (s); + var supports_browsing = device_supports_browsing (uuids); + var supports_file_transfer = device_supports_file_transfer (uuids); + + // update our lookup table with these new attributes + id_to_device.insert (id, new Device (id, + type, + name, + address, + icon, + is_connectable, + is_connected, + supports_browsing, + supports_file_transfer)); + + devices_changed (); + } + + public override void set_device_connected (uint id, bool connected) + { + var device = id_to_device.lookup (id); + var object_path = id_to_path.lookup (id); + var proxy = (object_path != null) ? path_to_proxy.lookup (object_path) : null; + + if ((proxy != null) && (device != null) && (device.is_connected != connected)) + { + if (connected) + { + device_connect (proxy as DBusProxy); + } + else // disconnect + { + try + { + proxy.disconnect (); + } + catch (Error e) + { + critical (@"Unable to disconnect $object_path: $(e.message)"); + } + } } - catch (Error e) - { - critical ("%s", e.message); - } } private void on_default_adapter_properties_changed () @@ -121,7 +326,7 @@ public class Bluez: Bluetooth } catch (Error e) { - critical ("%s", e.message); + critical (@"$(e.message)"); } this.powered = is_powered; @@ -136,7 +341,12 @@ public class Bluez: Bluetooth } catch (Error e) { - critical ("%s", e.message); + critical (@"$(e.message)"); } } + + public override List get_devices () + { + return id_to_device.get_values(); + } } diff --git a/src/desktop.vala b/src/desktop.vala index a7b8995..31fe9ac 100644 --- a/src/desktop.vala +++ b/src/desktop.vala @@ -19,11 +19,14 @@ class Desktop: Profile { + private uint idle_rebuild_id = 0; private Settings settings; private Bluetooth bluetooth; private SimpleAction root_action; private Action[] all_actions; + private Menu device_section; + private HashTable connect_actions; public override void add_actions_to_group (SimpleActionGroup group) { @@ -31,29 +34,136 @@ class Desktop: Profile group.insert (all_actions[i]); } + protected override void dispose () + { + if (idle_rebuild_id != 0) + { + Source.remove (idle_rebuild_id); + idle_rebuild_id = 0; + } + + base.dispose (); + } + public Desktop (Bluetooth bluetooth) { base ("desktop"); this.bluetooth = bluetooth; + connect_actions = new HashTable(direct_hash, direct_equal); + settings = new Settings ("com.canonical.indicator.bluetooth"); - root_action = new SimpleAction.stateful ("root-desktop", null, action_state_for_root()); + root_action = create_root_action (); all_actions = {}; all_actions += root_action; all_actions += create_enabled_action (bluetooth); all_actions += create_discoverable_action (bluetooth); - all_actions += create_settings_action (); all_actions += create_wizard_action (); + all_actions += create_browse_files_action (); + all_actions += create_send_file_action (); + all_actions += create_show_settings_action (); + + build_menu (); - bluetooth.notify.connect (() => update_root_action_state()); settings.changed["visible"].connect (()=> update_root_action_state()); + bluetooth.notify.connect (() => update_root_action_state()); + bluetooth.devices_changed.connect (()=> { + if (idle_rebuild_id == 0) + idle_rebuild_id = Idle.add (() => { + rebuild_device_section(); + idle_rebuild_id = 0; + return false; + }); + }); + } + + /// + /// MenuItems + /// + + MenuItem create_device_connection_menuitem (Device device) + { + var action_name = @"desktop-device-$(device.id)-connected"; + + var item = new MenuItem (_("Connection"), "indicator."+action_name); + item.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.switch"); + + // if this doesn't already have an action, create one + if (!connect_actions.contains (device.id)) + { + debug (@"creating action for $action_name"); + var action = new SimpleAction.stateful (action_name, null, device.is_connected); + action.activate.connect (() => action.set_state (!action.get_state().get_boolean())); + action.notify["state"].connect (() => bluetooth.set_device_connected (device.id, action.get_state().get_boolean())); + connect_actions.insert (device.id, action); + all_actions += action; + } + else + { + debug (@"updating action $(device.id) state to $(device.is_connected)"); + var action = connect_actions.lookup (device.id); + action.set_state (device.is_connected); + } + + return item; + } + + void rebuild_device_section () + { + device_section.remove_all (); + + foreach (var device in bluetooth.get_devices()) + { + Menu submenu = new Menu (); + MenuItem item; + + if (device.is_connectable) + submenu.append_item (create_device_connection_menuitem (device)); + + if (device.supports_browsing) + submenu.append (_("Browse files…"), @"indicator.desktop-browse-files::$(device.address)"); + + if (device.supports_file_transfer) + submenu.append (_("Send files…"), @"indicator.desktop-send-file::$(device.address)"); + + switch (device.device_type) + { + case Device.Type.KEYBOARD: + submenu.append (_("Keyboard Settings…"), "indicator.desktop-show-settings::keyboard"); + break; + case Device.Type.MOUSE: + case Device.Type.TABLET: + submenu.append (_("Mouse and Touchpad Settings…"), "indicator.desktop-show-settings::mouse"); + break; + + case Device.Type.HEADSET: + case Device.Type.HEADPHONES: + case Device.Type.OTHER_AUDIO: + submenu.append (_("Sound Settings…"), "indicator.desktop-show-settings::sound"); + break; + } + + /* only show the device if it's got actions that we can perform on it */ + if (submenu.get_n_items () > 0) + { + item = new MenuItem (device.name, null); + item.set_attribute_value ("icon", device.icon.serialize()); + item.set_submenu (submenu); + device_section.append_item (item); + } + } + } + + void build_menu () + { Menu section; MenuItem item; + // quick toggles section section = new Menu (); item = new MenuItem ("Bluetooth", "indicator.desktop-enabled"); item.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.switch"); @@ -63,12 +173,40 @@ class Desktop: Profile section.append_item (item); menu.append_section (null, section); + // devices section + device_section = new Menu (); + rebuild_device_section (); + menu.append_section (null, device_section); + + // settings section section = new Menu (); section.append (_("Set Up New Device…"), "indicator.desktop-wizard"); - section.append (_("Bluetooth Settings…"), "indicator.desktop-settings"); + section.append (_("Bluetooth Settings…"), "indicator.desktop-show-settings::bluetooth"); menu.append_section (null, section); } + /// + /// Action Helpers + /// + + void spawn_command_line_async (string command) + { + try { + Process.spawn_command_line_async (command); + } catch (Error e) { + warning ("unable to launch '$command': $(e.message)"); + } + } + + void show_control_center (string panel) + { + spawn_command_line_async ("gnome-control-center " + panel); + } + + /// + /// Actions + /// + Action create_enabled_action (Bluetooth bluetooth) { var action = new SimpleAction.stateful ("desktop-enabled", null, !bluetooth.blocked); @@ -89,15 +227,6 @@ class Desktop: Profile return action; } - void spawn_command_line_async (string command) - { - try { - Process.spawn_command_line_async (command); - } catch (Error e) { - warning ("unable to launch '%s': %s", command, e.message); - } - } - Action create_wizard_action () { var action = new SimpleAction ("desktop-wizard", null); @@ -105,10 +234,36 @@ class Desktop: Profile return action; } - Action create_settings_action () + Action create_browse_files_action () { - var action = new SimpleAction ("desktop-settings", null); - action.activate.connect (() => spawn_command_line_async ("gnome-control-center bluetooth")); + var action = new SimpleAction ("desktop-browse-files", VariantType.STRING); + action.activate.connect ((action, address) => { + var uri = @"obex://[$(address.get_string())]/"; + var file = File.new_for_uri (uri); + file.mount_enclosing_volume.begin (MountMountFlags.NONE, null, null, (obj, res) => { + try { + AppInfo.launch_default_for_uri (uri, null); + } catch (Error e) { + warning ("unable to launch '$uri': $(e.message)"); + } + }); + }); + return action; + } + + Action create_send_file_action () + { + var action = new SimpleAction ("desktop-send-file", VariantType.STRING); + action.activate.connect ((action, address) => { + spawn_command_line_async ("bluetooth-sendto --device=$(address.get_string())"); + }); + return action; + } + + Action create_show_settings_action () + { + var action = new SimpleAction ("desktop-show-settings", VariantType.STRING); + action.activate.connect ((action, panel) => show_control_center (panel.get_string())); return action; } @@ -143,6 +298,11 @@ class Desktop: Profile return builder.end (); } + SimpleAction create_root_action () + { + return new SimpleAction.stateful ("root-desktop", null, action_state_for_root()); + } + void update_root_action_state () { root_action.set_state (action_state_for_root ()); diff --git a/src/killswitch.vala b/src/killswitch.vala index cf5c6c8..fc7978c 100644 --- a/src/killswitch.vala +++ b/src/killswitch.vala @@ -22,8 +22,7 @@ * either by software (e.g., a session configuration setting) * or by hardware (e.g., user disabled it via a physical switch on her laptop). * - * The Bluetooth class uses this as a backend for its 'blocked' property. - * Other code can't even see this, so use Bluetooth.blocked instead. :) + * KillswitchBluetooth uses this as a backend for its Bluetooth.blocked property. */ public class KillSwitch: Object { @@ -50,7 +49,7 @@ public class RfKillSwitch: KillSwitch var bwritten = Posix.write (fd, &event, sizeof(Linux.RfKillEvent)); if (bwritten == -1) - warning ("Could not write rfkill event: %s", strerror(errno)); + warning (@"Could not write rfkill event: $(strerror(errno))"); } private class Entry @@ -90,7 +89,7 @@ public class RfKillSwitch: KillSwitch message ("fd is %d", fd); if (fd == -1) { - warning (@"Can't open $path: $(strerror(errno)); KillSwitch disable"); + warning (@"Can't open $path for use as a killswitch backend: $(strerror(errno))"); } else { diff --git a/src/main.vala b/src/main.vala index b88e94e..824e1d5 100644 --- a/src/main.vala +++ b/src/main.vala @@ -12,7 +12,7 @@ main (string[] args) var bluetooth = new Bluez (new RfKillSwitch ()); // start the service - var service = new BluetoothIndicator (bluetooth); + var service = new Service (bluetooth); service.run (); return Posix.EXIT_SUCCESS; diff --git a/src/phone.vala b/src/phone.vala index 8c9c816..34c10ab 100644 --- a/src/phone.vala +++ b/src/phone.vala @@ -48,7 +48,7 @@ class Phone: Profile try { Process.spawn_command_line_async ("system-settings bluetooth"); } catch (Error e) { - warning ("unable to launch settings: %s", e.message); + warning (@"unable to launch settings: $(e.message)"); } }); diff --git a/src/profile.vala b/src/profile.vala index 6dd5f52..360722d 100644 --- a/src/profile.vala +++ b/src/profile.vala @@ -43,12 +43,12 @@ class Profile: Object { try { - message ("exporting '%s' on %s", name, object_path); + debug (@"exporting '$name' on $object_path"); connection.export_menu_model (object_path, this.root); } catch (Error e) { - critical ("%s", e.message); + critical (@"Unable to export menu on $object_path: $(e.message)"); } } } diff --git a/src/service.vala b/src/service.vala index 10f7f25..0cece83 100644 --- a/src/service.vala +++ b/src/service.vala @@ -8,27 +8,13 @@ * See http://www.gnu.org/copyleft/gpl.html the full text of the license. */ -public class BluetoothIndicator +public class Service: Object { private MainLoop loop; private SimpleActionGroup actions; private HashTable profiles; - private DBusConnection bus; - private Indicator.Service indicator_service; - private Dbusmenu.Server menu_server; - private BluetoothService bluetooth_service; - private GnomeBluetooth.Client client; - private GnomeBluetooth.Killswitch killswitch; - private bool updating_killswitch = false; - private Dbusmenu.Menuitem enable_item; - private Dbusmenu.Menuitem visible_item; - private bool updating_visible = false; - private Dbusmenu.Menuitem devices_separator; - private List device_items; - private Dbusmenu.Menuitem menu; - - public BluetoothIndicator (Bluetooth bluetooth) + public Service (Bluetooth bluetooth) { profiles = new HashTable (str_hash, str_equal); profiles.insert ("phone", new Phone (bluetooth)); @@ -39,107 +25,6 @@ public class BluetoothIndicator profile.add_actions_to_group (actions); } - private void init_for_bus (DBusConnection bus) - { - this.bus = bus; - - /// - /// - - indicator_service = new Indicator.Service ("com.canonical.indicator.bluetooth.old"); - menu_server = new Dbusmenu.Server ("/com/canonical/indicator/bluetooth/menu"); - - bluetooth_service = new BluetoothService (); - bus.register_object ("/com/canonical/indicator/bluetooth/service", bluetooth_service); - - killswitch = new GnomeBluetooth.Killswitch (); - killswitch.state_changed.connect (killswitch_state_changed_cb); - - client = new GnomeBluetooth.Client (); - - menu = new Dbusmenu.Menuitem (); - menu_server.set_root (menu); - - enable_item = new Dbusmenu.Menuitem (); - enable_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Bluetooth")); - enable_item.property_set (Dbusmenu.MENUITEM_PROP_TYPE, "x-canonical-switch"); - enable_item.item_activated.connect (() => - { - if (updating_killswitch) - return; - if (killswitch.state == GnomeBluetooth.KillswitchState.UNBLOCKED) - killswitch.state = GnomeBluetooth.KillswitchState.SOFT_BLOCKED; - else - killswitch.state = GnomeBluetooth.KillswitchState.UNBLOCKED; - }); - menu.child_append (enable_item); - - visible_item = new Dbusmenu.Menuitem (); - visible_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Visible")); - visible_item.property_set (Dbusmenu.MENUITEM_PROP_TYPE, "x-canonical-switch"); - bool discoverable; - client.get ("default-adapter-discoverable", out discoverable); - visible_item.property_set_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE, discoverable ? Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED : Dbusmenu.MENUITEM_TOGGLE_STATE_UNCHECKED); - client.notify["default-adapter-discoverable"].connect (() => - { - updating_visible = true; - bool is_discoverable; - client.get ("default-adapter-discoverable", out is_discoverable); - visible_item.property_set_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE, is_discoverable ? Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED : Dbusmenu.MENUITEM_TOGGLE_STATE_UNCHECKED); - updating_visible = false; - }); - visible_item.item_activated.connect (() => - { - if (updating_visible) - return; - client.set ("default-adapter-discoverable", visible_item.property_get_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE) != Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED); - }); - menu.child_append (visible_item); - - devices_separator = new Dbusmenu.Menuitem (); - devices_separator.property_set (Dbusmenu.MENUITEM_PROP_TYPE, Dbusmenu.CLIENT_TYPES_SEPARATOR); - menu.child_append (devices_separator); - - device_items = new List (); - - client.model.row_inserted.connect (device_changed_cb); - client.model.row_changed.connect (device_changed_cb); - client.model.row_deleted.connect (device_removed_cb); - Gtk.TreeIter iter; - var have_iter = client.model.get_iter_first (out iter); - while (have_iter) - { - Gtk.TreeIter child_iter; - var have_child_iter = client.model.iter_children (out child_iter, iter); - while (have_child_iter) - { - device_changed_cb (null, child_iter); - have_child_iter = client.model.iter_next (ref child_iter); - } - have_iter = client.model.iter_next (ref iter); - } - - var sep = new Dbusmenu.Menuitem (); - sep.property_set (Dbusmenu.MENUITEM_PROP_TYPE, Dbusmenu.CLIENT_TYPES_SEPARATOR); - menu.child_append (sep); - - var item = new Dbusmenu.Menuitem (); - item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Set Up New Device…")); - item.item_activated.connect (() => { set_up_new_device (); }); - menu.child_append (item); - - item = new Dbusmenu.Menuitem (); - item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Bluetooth Settings…")); - item.item_activated.connect (() => { show_control_center ("bluetooth"); }); - menu.child_append (item); - - killswitch_state_changed_cb (killswitch.state); - - client.adapter_model.row_inserted.connect (update_visible); - client.adapter_model.row_deleted.connect (update_visible); - update_visible (); - } - public int run () { if (this.loop != null) @@ -162,310 +47,28 @@ public class BluetoothIndicator void on_bus_acquired (DBusConnection connection, string name) { - stdout.printf ("bus acquired: %s\n", name); - - init_for_bus (connection); + debug (@"bus acquired: $name"); + var object_path = "/com/canonical/indicator/bluetooth"; try { - connection.export_action_group ("/com/canonical/indicator/bluetooth", this.actions); + connection.export_action_group (object_path, this.actions); } catch (Error e) { - critical ("%s", e.message); + critical (@"Unable to export actions on $object_path: $(e.message)"); } - this.profiles.@foreach ((name,profile) => profile.export_menu (connection, @"/com/canonical/indicator/bluetooth/$name")); + this.profiles.for_each ((name,profile) => { + var path = @"/com/canonical/indicator/bluetooth/$name"; + message (@"exporting menu '$path'"); + profile.export_menu (connection, path); + }); } void on_name_lost (DBusConnection connection, string name) { - stdout.printf ("name lost: %s\n", name); + debug (@"name lost: $name"); this.loop.quit (); } - - - private BluetoothMenuItem? find_menu_item (string address) - { - foreach (var item in device_items) - if (item.address == address) - return item; - - return null; - } - - private void device_changed_cb (Gtk.TreePath? path, Gtk.TreeIter iter) - { - /* Ignore adapters */ - Gtk.TreeIter parent_iter; - if (!client.model.iter_parent (out parent_iter, iter)) - return; - - DBusProxy proxy; - string address; - string alias; - GnomeBluetooth.Type type; - string icon; - bool connected; - HashTable services; - string[] uuids; - client.model.get (iter, - GnomeBluetooth.Column.PROXY, out proxy, - GnomeBluetooth.Column.ADDRESS, out address, - GnomeBluetooth.Column.ALIAS, out alias, - GnomeBluetooth.Column.TYPE, out type, - GnomeBluetooth.Column.ICON, out icon, - GnomeBluetooth.Column.CONNECTED, out connected, - GnomeBluetooth.Column.SERVICES, out services, - GnomeBluetooth.Column.UUIDS, out uuids); - - /* Skip if haven't actually got any information yet */ - if (proxy == null) - return; - - /* Find or create menu item */ - var item = find_menu_item (address); - if (item == null) - { - item = new BluetoothMenuItem (client, address); - item.property_set_bool (Dbusmenu.MENUITEM_PROP_VISIBLE, killswitch.state == GnomeBluetooth.KillswitchState.UNBLOCKED); - var last_item = devices_separator as Dbusmenu.Menuitem; - if (device_items != null) - last_item = device_items.last ().data; - device_items.append (item); - menu.child_add_position (item, last_item.get_position (menu) + 1); - } - - item.update (type, proxy, alias, icon, connected, services, uuids); - } - - private void update_visible () - { - bluetooth_service._visible = client.adapter_model.iter_n_children (null) > 0;// && settings.get_boolean ("visible"); - var builder = new VariantBuilder (VariantType.ARRAY); - builder.add ("{sv}", "Visible", new Variant.boolean (bluetooth_service._visible)); - try - { - var properties = new Variant ("(sa{sv}as)", "com.canonical.indicator.bluetooth.service", builder, null); - bus.emit_signal (null, - "/com/canonical/indicator/bluetooth/service", - "org.freedesktop.DBus.Properties", - "PropertiesChanged", - properties); - } - catch (Error e) - { - warning ("Failed to emit signal: %s", e.message); - } - } - - private void device_removed_cb (Gtk.TreePath path) - { - Gtk.TreeIter iter; - if (!client.model.get_iter (out iter, path)) - return; - - string address; - client.model.get (iter, GnomeBluetooth.Column.ADDRESS, out address); - - var item = find_menu_item (address); - if (item == null) - return; - - device_items.remove (item); - menu.child_delete (item); - } - - private void killswitch_state_changed_cb (GnomeBluetooth.KillswitchState state) - { - updating_killswitch = true; - - var enabled = state == GnomeBluetooth.KillswitchState.UNBLOCKED; - - bluetooth_service._icon_name = enabled ? "bluetooth-active" : "bluetooth-disabled"; - bluetooth_service._accessible_description = enabled ? _("Bluetooth: On") : _("Bluetooth: Off"); - - var builder = new VariantBuilder (VariantType.ARRAY); - builder.add ("{sv}", "IconName", new Variant.string (bluetooth_service._icon_name)); - builder.add ("{sv}", "AccessibleDescription", new Variant.string (bluetooth_service._accessible_description)); - try - { - var properties = new Variant ("(sa{sv}as)", "com.canonical.indicator.bluetooth.service", builder, null); - bus.emit_signal (null, - "/com/canonical/indicator/bluetooth/service", - "org.freedesktop.DBus.Properties", - "PropertiesChanged", - properties); - } - catch (Error e) - { - warning ("Failed to emit signal: %s", e.message); - } - - enable_item.property_set_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE, enabled ? Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED : Dbusmenu.MENUITEM_TOGGLE_STATE_UNCHECKED); - - /* Disable devices when locked */ - visible_item.property_set_bool (Dbusmenu.MENUITEM_PROP_VISIBLE, enabled); - devices_separator.property_set_bool (Dbusmenu.MENUITEM_PROP_VISIBLE, enabled); - foreach (var item in device_items) - item.property_set_bool (Dbusmenu.MENUITEM_PROP_VISIBLE, enabled && item.get_children () != null); - - updating_killswitch = false; - } -} - -private class BluetoothMenuItem : Dbusmenu.Menuitem -{ - private GnomeBluetooth.Client client; - public string address; - private Dbusmenu.Menuitem? connect_item = null; - private bool make_submenu = false; - - public BluetoothMenuItem (GnomeBluetooth.Client client, string address) - { - this.client = client; - this.address = address; - } - - public void update (GnomeBluetooth.Type type, DBusProxy proxy, string alias, string icon, bool connected, HashTable? services, string[] uuids) - { - property_set (Dbusmenu.MENUITEM_PROP_LABEL, alias); - property_set (Dbusmenu.MENUITEM_PROP_ICON_NAME, icon); - if (connect_item != null) - connect_item.property_set_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE, connected ? Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED : Dbusmenu.MENUITEM_TOGGLE_STATE_UNCHECKED); - - /* FIXME: Not sure if the GUI elements below can change over time */ - if (make_submenu) - return; - make_submenu = true; - - if (services != null) - { - connect_item = new Dbusmenu.Menuitem (); - connect_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Connection")); - connect_item.property_set (Dbusmenu.MENUITEM_PROP_TYPE, "x-canonical-switch"); - connect_item.property_set_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE, connected ? Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED : Dbusmenu.MENUITEM_TOGGLE_STATE_UNCHECKED); - connect_item.item_activated.connect (() => { connect_service (proxy.get_object_path (), connect_item.property_get_int (Dbusmenu.MENUITEM_PROP_TOGGLE_STATE) != Dbusmenu.MENUITEM_TOGGLE_STATE_CHECKED); }); - child_append (connect_item); - } - - var can_send = false; - var can_browse = false; - if (uuids != null) - { - for (var i = 0; uuids[i] != null; i++) - { -message ("alias %s uuid #%d: %s", alias, i, uuids[i]); - if (uuids[i] == "OBEXObjectPush") - can_send = true; - if (uuids[i] == "OBEXFileTransfer") - can_browse = true; - } - } - - if (can_send) - { - var send_item = new Dbusmenu.Menuitem (); - send_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Send files…")); - send_item.item_activated.connect (() => { GnomeBluetooth.send_to_address (address, alias); }); - child_append (send_item); - } - - if (can_browse) - { - var browse_item = new Dbusmenu.Menuitem (); - browse_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Browse files…")); - browse_item.item_activated.connect (() => { GnomeBluetooth.browse_address (null, address, Gdk.CURRENT_TIME, null); }); - child_append (browse_item); - } - - switch (type) - { - case GnomeBluetooth.Type.KEYBOARD: - var keyboard_item = new Dbusmenu.Menuitem (); - keyboard_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Keyboard Settings…")); - keyboard_item.item_activated.connect (() => { show_control_center ("keyboard"); }); - child_append (keyboard_item); - break; - - case GnomeBluetooth.Type.MOUSE: - case GnomeBluetooth.Type.TABLET: - var mouse_item = new Dbusmenu.Menuitem (); - mouse_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Mouse and Touchpad Settings…")); - mouse_item.item_activated.connect (() => { show_control_center ("mouse"); }); - child_append (mouse_item); - break; - - case GnomeBluetooth.Type.HEADSET: - case GnomeBluetooth.Type.HEADPHONES: - case GnomeBluetooth.Type.OTHER_AUDIO: - var sound_item = new Dbusmenu.Menuitem (); - sound_item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, _("Sound Settings…")); - sound_item.item_activated.connect (() => { show_control_center ("sound"); }); - child_append (sound_item); - break; - } - - property_set_bool (Dbusmenu.MENUITEM_PROP_VISIBLE, get_children () != null); - } - - private void connect_service (string device, bool connect) - { - client.connect_service.begin (device, connect, null, (object, result) => - { - var connected = false; - try - { - connected = client.connect_service.end (result); - } - catch (Error e) - { - warning ("Failed to connect service: %s", e.message); - } - }); - } -} - -private void set_up_new_device () -{ - try - { - Process.spawn_command_line_async ("bluetooth-wizard"); - } - catch (GLib.SpawnError e) - { - warning ("Failed to open bluetooth-wizard: %s", e.message); - } -} - -private void show_control_center (string panel) -{ - try - { - Process.spawn_command_line_async ("gnome-control-center %s".printf (panel)); - } - catch (GLib.SpawnError e) - { - warning ("Failed to open control center: %s", e.message); - } -} - -[DBus (name = "com.canonical.indicator.bluetooth.service")] -private class BluetoothService : Object -{ - internal bool _visible = false; - public bool visible - { - get { return _visible; } - } - internal string _icon_name = "bluetooth-active"; - public string icon_name - { - get { return _icon_name; } - } - internal string _accessible_description = _("Bluetooth"); - public string accessible_description - { - get { return _accessible_description; } - } } -- cgit v1.2.3 From ccc56573d73b7201f3db177824b48c25ce7663b5 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 5 Aug 2013 17:01:23 -0500 Subject: edit killswitch for readability and to remove the iowatch in dispose() --- src/killswitch.vala | 52 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/killswitch.vala b/src/killswitch.vala index fc7978c..2c4dfff 100644 --- a/src/killswitch.vala +++ b/src/killswitch.vala @@ -22,25 +22,27 @@ * either by software (e.g., a session configuration setting) * or by hardware (e.g., user disabled it via a physical switch on her laptop). * - * KillswitchBluetooth uses this as a backend for its Bluetooth.blocked property. + * KillSwitchBluetooth uses this as a backend for its Bluetooth.blocked property. */ -public class KillSwitch: Object +public interface KillSwitch: Object { - public bool blocked { get; protected set; default = false; } + public abstract bool blocked { get; protected set; } - public virtual void try_set_blocked (bool blocked) {} + public abstract void try_set_blocked (bool blocked); } /** - * On Linux systems, monitors /dev/rfkill to watch for bluetooth blockage + * KillSwitch impementation for Linux using /dev/rfkill */ -public class RfKillSwitch: KillSwitch +public class RfKillSwitch: KillSwitch, Object { - public override void try_set_blocked (bool blocked) + public bool blocked { get; protected set; default = false; } + + public void try_set_blocked (bool blocked) { return_if_fail (this.blocked != blocked); - // write a 'soft kill' event to fkill + // try to soft-block all the bluetooth devices var event = Linux.RfKillEvent() { op = Linux.RfKillOp.CHANGE_ALL, type = Linux.RfKillType.BLUETOOTH, @@ -52,6 +54,7 @@ public class RfKillSwitch: KillSwitch warning (@"Could not write rfkill event: $(strerror(errno))"); } + /* represents an entry that we've read from the rfkill file */ private class Entry { public uint32 idx; @@ -61,23 +64,25 @@ public class RfKillSwitch: KillSwitch } private HashTable entries; - private int fd; + private int fd = -1; private IOChannel channel; private uint watch; - private bool calculate_blocked () + protected override void dispose () { - foreach (Entry entry in entries.get_values()) - if (entry.soft || entry.hard) - return true; + if (watch != 0) + { + Source.remove (watch); + watch = 0; + } - return false; - } + if (fd != -1) + { + Posix.close (fd); + fd = -1; + } - ~RfKillSwitch () - { - Source.remove (watch); - Posix.close (fd); + base.dispose (); } public RfKillSwitch () @@ -147,7 +152,12 @@ public class RfKillSwitch: KillSwitch break; } - // update the 'blocked' property - blocked = calculate_blocked (); + /* update our blocked property. + it should be true if any bluetooth entry is hard- or soft-blocked */ + var b = false; + foreach (Entry entry in entries.get_values()) + if ((b = (entry.soft || entry.hard))) + break; + blocked = b; } } -- cgit v1.2.3 From fcf74c76f44201684dedd88e1fce39140c1a6235 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 5 Aug 2013 17:46:44 -0500 Subject: silence g_message scaffolding --- src/bluez.vala | 2 +- src/killswitch.vala | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/bluez.vala b/src/bluez.vala index b4d7836..fa4b9ea 100644 --- a/src/bluez.vala +++ b/src/bluez.vala @@ -59,7 +59,7 @@ public class Bluez: KillswitchBluetooth { if (object_path != null) try { - message (@"using default adapter at $object_path"); + debug (@"using default adapter at $object_path"); default_adapter = Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", object_path); default_adapter.property_changed.connect(() => on_default_adapter_properties_changed()); diff --git a/src/killswitch.vala b/src/killswitch.vala index 2c4dfff..7695c0c 100644 --- a/src/killswitch.vala +++ b/src/killswitch.vala @@ -91,7 +91,6 @@ public class RfKillSwitch: KillSwitch, Object var path = "/dev/rfkill"; fd = Posix.open (path, Posix.O_RDWR | Posix.O_NONBLOCK ); - message ("fd is %d", fd); if (fd == -1) { warning (@"Can't open $path for use as a killswitch backend: $(strerror(errno))"); -- cgit v1.2.3 From 77e298f113541feacb27c1deca14ec953b6559b0 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 5 Aug 2013 17:47:31 -0500 Subject: pass the SimpleActionGroup handle into the profile object so that dynamically-added actions can get added/exported --- src/desktop.vala | 34 +++++++++++++++++----------------- src/phone.vala | 19 ++++++++++--------- src/profile.vala | 2 -- src/service.vala | 12 +++++------- 4 files changed, 32 insertions(+), 35 deletions(-) (limited to 'src') diff --git a/src/desktop.vala b/src/desktop.vala index 31fe9ac..4432a3f 100644 --- a/src/desktop.vala +++ b/src/desktop.vala @@ -22,18 +22,12 @@ class Desktop: Profile private uint idle_rebuild_id = 0; private Settings settings; private Bluetooth bluetooth; + private SimpleActionGroup action_group; private SimpleAction root_action; - private Action[] all_actions; private Menu device_section; private HashTable connect_actions; - public override void add_actions_to_group (SimpleActionGroup group) - { - for (var i=0; i(direct_hash, direct_equal); @@ -57,19 +52,24 @@ class Desktop: Profile root_action = create_root_action (); - all_actions = {}; - all_actions += root_action; - all_actions += create_enabled_action (bluetooth); - all_actions += create_discoverable_action (bluetooth); - all_actions += create_wizard_action (); - all_actions += create_browse_files_action (); - all_actions += create_send_file_action (); - all_actions += create_show_settings_action (); + // build the static actions + Action[] actions = {}; + actions += root_action; + actions += create_enabled_action (bluetooth); + actions += create_discoverable_action (bluetooth); + actions += create_wizard_action (); + actions += create_browse_files_action (); + actions += create_send_file_action (); + actions += create_show_settings_action (); + foreach (var a in actions) + action_group.insert (a); build_menu (); settings.changed["visible"].connect (()=> update_root_action_state()); bluetooth.notify.connect (() => update_root_action_state()); + + // when devices change, rebuild our device section bluetooth.devices_changed.connect (()=> { if (idle_rebuild_id == 0) idle_rebuild_id = Idle.add (() => { @@ -99,7 +99,7 @@ class Desktop: Profile action.activate.connect (() => action.set_state (!action.get_state().get_boolean())); action.notify["state"].connect (() => bluetooth.set_device_connected (device.id, action.get_state().get_boolean())); connect_actions.insert (device.id, action); - all_actions += action; + action_group.insert (action); } else { diff --git a/src/phone.vala b/src/phone.vala index 34c10ab..4eaff4d 100644 --- a/src/phone.vala +++ b/src/phone.vala @@ -19,21 +19,22 @@ class Phone: Profile { - private Action[] actions; + Bluetooth bluetooth; + SimpleActionGroup action_group; - public override void add_actions_to_group (SimpleActionGroup group) - { - for (var i=0; i (str_hash, str_equal); - profiles.insert ("phone", new Phone (bluetooth)); - profiles.insert ("desktop", new Desktop (bluetooth)); - actions = new SimpleActionGroup (); - foreach (Profile profile in profiles.get_values()) - profile.add_actions_to_group (actions); + + profiles = new HashTable (str_hash, str_equal); + profiles.insert ("phone", new Phone (bluetooth, actions)); + profiles.insert ("desktop", new Desktop (bluetooth, actions)); } public int run () @@ -61,7 +59,7 @@ public class Service: Object this.profiles.for_each ((name,profile) => { var path = @"/com/canonical/indicator/bluetooth/$name"; - message (@"exporting menu '$path'"); + debug (@"exporting menu '$path'"); profile.export_menu (connection, path); }); } -- cgit v1.2.3 From 76efb05ae0ad6111adb1cb127069d97bf2eb61da Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 5 Aug 2013 18:24:41 -0500 Subject: copyediting: readability + grouping related methods together --- src/bluez.vala | 177 ++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 106 insertions(+), 71 deletions(-) (limited to 'src') diff --git a/src/bluez.vala b/src/bluez.vala index fa4b9ea..046362b 100644 --- a/src/bluez.vala +++ b/src/bluez.vala @@ -22,13 +22,21 @@ */ public class Bluez: KillswitchBluetooth { - private org.bluez.Manager manager; - private org.bluez.Adapter default_adapter; - private HashTable path_to_proxy; - private HashTable path_to_id; - private HashTable id_to_path; - private HashTable id_to_device; - private uint next_device_id = 1; + uint next_device_id = 1; + org.bluez.Manager manager; + org.bluez.Adapter default_adapter; + + /* maps an org.bluez.Device's object_path to the org.bluez.Device proxy */ + HashTable path_to_proxy; + + /* maps an org.bluez.Device's object_path to our arbitrary unique id */ + HashTable path_to_id; + + /* maps our arbitrary unique id to an org.bluez.Device's object path */ + HashTable id_to_path; + + /* maps our arbitrary unique id to a Bluetooth.Device struct for public consumption */ + HashTable id_to_device; public Bluez (KillSwitch killswitch) { @@ -44,8 +52,10 @@ public class Bluez: KillswitchBluetooth try { manager = Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", "/"); - manager.default_adapter_changed.connect ((object_path) => on_default_adapter_changed (object_path)); + + // get the current default adapter, and watch for future default adapters adapter_path = manager.default_adapter (); + manager.default_adapter_changed.connect ((object_path) => on_default_adapter_changed (object_path)); } catch (Error e) { @@ -80,9 +90,37 @@ public class Bluez: KillswitchBluetooth critical (@"$(e.message)"); } - this.on_default_adapter_properties_changed (); + on_default_adapter_properties_changed (); + } + + private void on_default_adapter_properties_changed () + { + bool is_discoverable = false; + bool is_powered = false; + + if (default_adapter != null) try + { + var properties = default_adapter.get_properties(); + + var v = properties.lookup("Discoverable"); + is_discoverable = (v != null) && v.get_boolean (); + + v = properties.lookup("Powered"); + is_powered = (v != null) && v.get_boolean (); + } + catch (Error e) + { + critical (@"$(e.message)"); + } + + powered = is_powered; + discoverable = is_discoverable; } + //// + //// bluetooth device UUIDs + //// + private static uint16 get_uuid16_from_uuid_string (string uuid) { uint16 uuid16; @@ -116,10 +154,14 @@ public class Bluez: KillswitchBluetooth return false; } - /* headsets, audio sinks, and input devices are connectable. + //// + //// Connectable Interfaces + //// + + /* Headsets, Audio Sinks, and Input devices are connectable. * - * TODO: this duplicates the behavior of the indicator from when it used - * gnome-bluetooth as a backend. Are there other interfaces we care about? */ + * This continues the behavior of the old gnome-bluetooth indicator. + * But are there other interfaces we care about? */ private DBusInterfaceInfo[] get_connectable_interfaces (DBusProxy device) { DBusInterfaceInfo[] connectable_interfaces = {}; @@ -157,36 +199,54 @@ public class Bluez: KillswitchBluetooth private bool device_is_connectable (DBusProxy device) { - var connectable_interfaces = get_connectable_interfaces (device); - return connectable_interfaces.length > 0; + return get_connectable_interfaces (device).length > 0; } - private void device_connect (DBusProxy proxy) + // call "Connect" on the specified interface + private void device_connect_on_interface (DBusProxy proxy, string interface_name) { - var connection = proxy.get_connection (); + var bus = proxy.get_connection (); var object_path = proxy.get_object_path (); + debug (@"trying to connect to $object_path: $(interface_name)"); + + try + { + bus.call_sync ("org.bluez", object_path, interface_name, + "Connect", null, null, DBusCallFlags.NONE, -1); + } + catch (Error e) + { + debug (@"Unable to call $interface_name.Connect() on $object_path: $(e.message)"); + } + } + + private void device_connect (org.bluez.Device device) + { + DBusProxy proxy = device as DBusProxy; + + // call "Connect" on all the interfaces that support it foreach (var i in get_connectable_interfaces (proxy)) + device_connect_on_interface (proxy, i.name); + } + + private void device_disconnect (org.bluez.Device device) + { + try { - try - { - debug (@"trying to connect to $object_path: $(i.name)"); - connection.call_sync ("org.bluez", - object_path, - i.name, - "Connect", - null, - null, - DBusCallFlags.NONE, - -1); - } - catch (Error e) - { - debug (@"Unable to call $(i.name).Connect() on $(proxy.get_object_path()): $(e.message)"); - } + device.disconnect (); + } + catch (Error e) + { + var object_path = (device as DBusProxy).get_object_path (); + critical (@"Unable to disconnect $object_path: $(e.message)"); } } + //// + //// Device Upkeep + //// + private void add_device (string object_path) { if (!path_to_proxy.contains (object_path)) @@ -205,6 +265,12 @@ public class Bluez: KillswitchBluetooth } } + /* Update our public Device struct from the org.bluez.Device's properties. + * + * This is called when we first walk through bluez' Devices on startup, + * when the org.bluez.Adapter gets a new device, + * and when a device's properties change s.t. we need to rebuild the proxy. + */ private void update_device (org.bluez.Device device_proxy) { HashTable properties; @@ -283,6 +349,10 @@ public class Bluez: KillswitchBluetooth devices_changed (); } + //// + //// Public API + //// + public override void set_device_connected (uint id, bool connected) { var device = id_to_device.lookup (id); @@ -292,52 +362,17 @@ public class Bluez: KillswitchBluetooth if ((proxy != null) && (device != null) && (device.is_connected != connected)) { if (connected) - { - device_connect (proxy as DBusProxy); - } - else // disconnect - { - try - { - proxy.disconnect (); - } - catch (Error e) - { - critical (@"Unable to disconnect $object_path: $(e.message)"); - } - } + device_connect (proxy); + else + device_disconnect (proxy); } } - private void on_default_adapter_properties_changed () - { - bool is_discoverable = false; - bool is_powered = false; - - if (this.default_adapter != null) try - { - var properties = this.default_adapter.get_properties(); - - var v = properties.lookup("Discoverable"); - is_discoverable = (v != null) && v.get_boolean (); - - v = properties.lookup("Powered"); - is_powered = (v != null) && v.get_boolean (); - } - catch (Error e) - { - critical (@"$(e.message)"); - } - - this.powered = is_powered; - this.discoverable = is_discoverable; - } - public override void try_set_discoverable (bool b) { if (discoverable != b) try { - this.default_adapter.set_property ("Discoverable", new Variant.boolean(b)); + default_adapter.set_property ("Discoverable", new Variant.boolean(b)); } catch (Error e) { -- cgit v1.2.3 From 2d03420649eb330e7bae73929d98f0a8ea755ff7 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 5 Aug 2013 18:31:09 -0500 Subject: in phone and desktop, make 'action_state_for_root' private -- it's no longer in the Profile superclass. --- src/desktop.vala | 4 ++-- src/phone.vala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/desktop.vala b/src/desktop.vala index 4432a3f..5eb914b 100644 --- a/src/desktop.vala +++ b/src/desktop.vala @@ -88,7 +88,7 @@ class Desktop: Profile { var action_name = @"desktop-device-$(device.id)-connected"; - var item = new MenuItem (_("Connection"), "indicator."+action_name); + var item = new MenuItem (_("Connection"), @"indicator.$action_name"); item.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.switch"); // if this doesn't already have an action, create one @@ -267,7 +267,7 @@ class Desktop: Profile return action; } - protected Variant action_state_for_root () + private Variant action_state_for_root () { bool blocked = bluetooth.blocked; bool powered = bluetooth.powered; diff --git a/src/phone.vala b/src/phone.vala index 4eaff4d..de5febe 100644 --- a/src/phone.vala +++ b/src/phone.vala @@ -56,7 +56,7 @@ class Phone: Profile return action; } - protected Variant action_state_for_root () + private Variant action_state_for_root () { var label = "Hello World"; // FIXME var a11y = "Hello World"; // FIXME -- cgit v1.2.3 From 6492053ff654afbac9a078b7b67cca4a6dabecec Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 5 Aug 2013 19:36:17 -0500 Subject: promote shared functions up from Desktop to Profile so that Phone can use it too --- src/desktop.vala | 95 +++++++++---------------------------------------- src/phone.vala | 53 +++++++++++++--------------- src/profile.vala | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 139 insertions(+), 114 deletions(-) (limited to 'src') diff --git a/src/desktop.vala b/src/desktop.vala index 5eb914b..24d7f9f 100644 --- a/src/desktop.vala +++ b/src/desktop.vala @@ -21,10 +21,8 @@ class Desktop: Profile { private uint idle_rebuild_id = 0; private Settings settings; - private Bluetooth bluetooth; private SimpleActionGroup action_group; - private SimpleAction root_action; private Menu device_section; private HashTable connect_actions; @@ -41,20 +39,19 @@ class Desktop: Profile public Desktop (Bluetooth bluetooth, SimpleActionGroup action_group) { - base ("desktop"); + const string profile_name = "desktop"; + + base (bluetooth, profile_name); - this.bluetooth = bluetooth; this.action_group = action_group; connect_actions = new HashTable(direct_hash, direct_equal); settings = new Settings ("com.canonical.indicator.bluetooth"); - root_action = create_root_action (); - // build the static actions Action[] actions = {}; - actions += root_action; + actions += get_root_action (profile_name); actions += create_enabled_action (bluetooth); actions += create_discoverable_action (bluetooth); actions += create_wizard_action (); @@ -66,8 +63,10 @@ class Desktop: Profile build_menu (); - settings.changed["visible"].connect (()=> update_root_action_state()); - bluetooth.notify.connect (() => update_root_action_state()); + // know when to show the indicator & when to hide it + settings.changed["visible"].connect (()=> update_visibility()); + bluetooth.notify.connect (() => update_visibility()); + update_visibility (); // when devices change, rebuild our device section bluetooth.devices_changed.connect (()=> { @@ -80,6 +79,11 @@ class Desktop: Profile }); } + void update_visibility () + { + visible = bluetooth.powered && !bluetooth.blocked && settings.get_boolean("visible"); + } + /// /// MenuItems /// @@ -165,9 +169,7 @@ class Desktop: Profile // quick toggles section section = new Menu (); - item = new MenuItem ("Bluetooth", "indicator.desktop-enabled"); - item.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.switch"); - section.append_item (item); + section.append_item (create_enabled_menuitem ()); item = new MenuItem ("Visible", "indicator.desktop-discoverable"); item.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.switch"); section.append_item (item); @@ -185,35 +187,13 @@ class Desktop: Profile menu.append_section (null, section); } - /// - /// Action Helpers - /// - - void spawn_command_line_async (string command) - { - try { - Process.spawn_command_line_async (command); - } catch (Error e) { - warning ("unable to launch '$command': $(e.message)"); - } - } - - void show_control_center (string panel) - { - spawn_command_line_async ("gnome-control-center " + panel); - } - /// /// Actions /// - Action create_enabled_action (Bluetooth bluetooth) + void show_settings (string panel) { - var action = new SimpleAction.stateful ("desktop-enabled", null, !bluetooth.blocked); - action.activate.connect (() => action.set_state (!action.get_state().get_boolean())); - action.notify["state"].connect (() => bluetooth.try_set_blocked (!action.get_state().get_boolean())); - bluetooth.notify["blocked"].connect (() => action.set_state (!bluetooth.blocked)); - return action; + spawn_command_line_async ("gnome-control-center " + panel); } Action create_discoverable_action (Bluetooth bluetooth) @@ -263,48 +243,7 @@ class Desktop: Profile Action create_show_settings_action () { var action = new SimpleAction ("desktop-show-settings", VariantType.STRING); - action.activate.connect ((action, panel) => show_control_center (panel.get_string())); + action.activate.connect ((action, panel) => show_settings (panel.get_string())); return action; } - - private Variant action_state_for_root () - { - bool blocked = bluetooth.blocked; - bool powered = bluetooth.powered; - - settings.changed["visible"].connect (()=> update_root_action_state()); - - bool visible = powered && settings.get_boolean("visible"); - - string a11y; - string icon_name; - if (powered && !blocked) - { - a11y = "Bluetooth (on)"; - icon_name = "bluetooth-active"; - } - else - { - a11y = "Bluetooth (off)"; - icon_name = "bluetooth-disabled"; - } - - var icon = new ThemedIcon.with_default_fallbacks (icon_name); - - var builder = new VariantBuilder (new VariantType ("a{sv}")); - builder.add ("{sv}", "visible", new Variant ("b", visible)); - builder.add ("{sv}", "accessible-desc", new Variant ("s", a11y)); - builder.add ("{sv}", "icon", icon.serialize()); - return builder.end (); - } - - SimpleAction create_root_action () - { - return new SimpleAction.stateful ("root-desktop", null, action_state_for_root()); - } - - void update_root_action_state () - { - root_action.set_state (action_state_for_root ()); - } } diff --git a/src/phone.vala b/src/phone.vala index de5febe..05e6b9d 100644 --- a/src/phone.vala +++ b/src/phone.vala @@ -19,57 +19,54 @@ class Phone: Profile { - Bluetooth bluetooth; SimpleActionGroup action_group; public Phone (Bluetooth bluetooth, SimpleActionGroup action_group) { - base ("phone"); + const string profile_name = "phone"; + base (bluetooth, profile_name); this.bluetooth = bluetooth; this.action_group = action_group; // build the static actions Action[] actions = {}; - actions += new SimpleAction.stateful ("root-phone", null, action_state_for_root()); + actions += get_root_action (profile_name); + actions += create_enabled_action (bluetooth); actions += create_settings_action (); foreach (var a in actions) action_group.insert (a); var section = new Menu (); - section.append (_("Sound settings…"), "indicator.phone-settings"); + section.append_item (create_enabled_menuitem ()); + section.append (_("Bluetooth settings…"), "indicator.phone-show-settings::bluetooth"); menu.append_section (null, section); + + // know when to show the indicator & when to hide it + bluetooth.notify.connect (() => update_visibility()); + update_visibility (); + + bluetooth.notify.connect (() => update_root_action_state()); } - Action create_settings_action () + void update_visibility () { - var action = new SimpleAction ("phone-settings", null); + visible = bluetooth.powered && !bluetooth.blocked; + } - action.activate.connect ((action, param) => { - try { - Process.spawn_command_line_async ("system-settings bluetooth"); - } catch (Error e) { - warning (@"unable to launch settings: $(e.message)"); - } - }); + /// + /// Actions + /// - return action; + void show_settings (string panel) + { + spawn_command_line_async ("system-settings " + panel); } - private Variant action_state_for_root () + Action create_settings_action () { - var label = "Hello World"; // FIXME - var a11y = "Hello World"; // FIXME - var visible = true; // FIXME - - string icon_name = "bluetooth"; // FIXME: enabled, disabled, connected, etc. - var icon = new ThemedIcon.with_default_fallbacks (icon_name); - - var builder = new VariantBuilder (new VariantType ("a{sv}")); - builder.add ("{sv}", "visible", new Variant ("b", visible)); - builder.add ("{sv}", "label", new Variant ("s", label)); - builder.add ("{sv}", "accessible-desc", new Variant ("s", a11y)); - builder.add ("{sv}", "icon", icon.serialize()); - return builder.end (); + var action = new SimpleAction ("phone-show-settings", VariantType.STRING); + action.activate.connect ((action, panel) => show_settings (panel.get_string())); + return action; } } diff --git a/src/profile.vala b/src/profile.vala index d839121..74c8242 100644 --- a/src/profile.vala +++ b/src/profile.vala @@ -19,29 +19,32 @@ class Profile: Object { - protected string name; + protected Bluetooth bluetooth; + protected string profile_name; protected Menu root; protected Menu menu; - public Profile (string name) + protected bool visible { get; set; default = true; } + + public Profile (Bluetooth bluetooth, string profile_name) { - this.name = name; + this.bluetooth = bluetooth; + this.profile_name = profile_name; menu = new Menu (); - var root_item = new MenuItem (null, "indicator.root-" + name); - root_item.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.root"); - root_item.set_submenu (menu); + var item = create_root_menuitem (); + item.set_submenu (menu); root = new Menu (); - root.append_item (root_item); + root.append_item (item); } public void export_menu (DBusConnection connection, string object_path) { try { - debug (@"exporting '$name' on $object_path"); + debug (@"exporting '$profile_name' on $object_path"); connection.export_menu_model (object_path, this.root); } catch (Error e) @@ -49,4 +52,90 @@ class Profile: Object critical (@"Unable to export menu on $object_path: $(e.message)"); } } + + protected void spawn_command_line_async (string command) + { + try { + Process.spawn_command_line_async (command); + } catch (Error e) { + warning (@"Unable to launch '$command': $(e.message)"); + } + } + + /// + /// Menu Items + /// + + protected MenuItem create_enabled_menuitem () + { + MenuItem item = new MenuItem ("Bluetooth", "indicator.bluetooth-enabled"); + item.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.switch"); + return item; + } + + private MenuItem create_root_menuitem () + { + var item = new MenuItem (null, @"indicator.root-$profile_name"); + item.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.root"); + return item; + } + + /// + /// Actions + /// + + protected Action create_enabled_action (Bluetooth bluetooth) + { + var action = new SimpleAction.stateful ("bluetooth-enabled", null, !bluetooth.blocked); + action.activate.connect (() => action.set_state (!action.get_state().get_boolean())); + action.notify["state"].connect (() => bluetooth.try_set_blocked (!action.get_state().get_boolean())); + bluetooth.notify["blocked"].connect (() => action.set_state (!bluetooth.blocked)); + return action; + } + + protected SimpleAction root_action = null; + + protected SimpleAction get_root_action (string profile) + { + if (root_action == null) + { + root_action = new SimpleAction.stateful (@"root-$profile", null, action_state_for_root()); + + this.notify["visible"].connect (() => update_root_action_state()); + } + + return root_action; + } + + protected void update_root_action_state () + { + root_action.set_state (action_state_for_root ()); + } + + protected Variant action_state_for_root () + { + bool blocked = bluetooth.blocked; + bool powered = bluetooth.powered; + + string a11y; + string icon_name; + if (powered && !blocked) + { + a11y = "Bluetooth (on)"; + icon_name = "bluetooth-active"; + } + else + { + a11y = "Bluetooth (off)"; + icon_name = "bluetooth-disabled"; + } + + var icon = new ThemedIcon.with_default_fallbacks (icon_name); + + var builder = new VariantBuilder (new VariantType ("a{sv}")); + builder.add ("{sv}", "visible", new Variant.boolean (visible)); + builder.add ("{sv}", "accessible-desc", new Variant.string (a11y)); + builder.add ("{sv}", "icon", icon.serialize()); + return builder.end (); + } } -- cgit v1.2.3 From bae23fbcec140cecb5d69047bc5262dc4620cafb Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 5 Aug 2013 19:42:55 -0500 Subject: copyediting: fix indentation in Service, fix copyright & author comments in bluez, service, main --- src/bluez.vala | 2 + src/main.vala | 21 +++++++++- src/service.vala | 122 ++++++++++++++++++++++++++++++------------------------- 3 files changed, 87 insertions(+), 58 deletions(-) (limited to 'src') diff --git a/src/bluez.vala b/src/bluez.vala index 046362b..d84d869 100644 --- a/src/bluez.vala +++ b/src/bluez.vala @@ -15,8 +15,10 @@ * * Authors: * Charles Kerr + * Robert Ancell */ + /** * Bluetooth implementaion which uses org.bluez on DBus */ diff --git a/src/main.vala b/src/main.vala index 824e1d5..fdd08e8 100644 --- a/src/main.vala +++ b/src/main.vala @@ -1,6 +1,23 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authors: + * Charles Kerr + */ -public static int -main (string[] args) +public static int main (string[] args) { // set up i18n Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8"); diff --git a/src/service.vala b/src/service.vala index 69f57a6..767343f 100644 --- a/src/service.vala +++ b/src/service.vala @@ -1,72 +1,82 @@ /* - * Copyright (C) 2012-2013 Canonical Ltd. - * Author: Robert Ancell + * Copyright 2013 Canonical Ltd. * - * This program is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation, version 3 of the License. - * See http://www.gnu.org/copyleft/gpl.html the full text of the license. + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authors: + * Charles Kerr + * Robert Ancell */ public class Service: Object { - private MainLoop loop; - private SimpleActionGroup actions; - private HashTable profiles; + private MainLoop loop; + private SimpleActionGroup actions; + private HashTable profiles; - public Service (Bluetooth bluetooth) - { - actions = new SimpleActionGroup (); + public Service (Bluetooth bluetooth) + { + actions = new SimpleActionGroup (); - profiles = new HashTable (str_hash, str_equal); - profiles.insert ("phone", new Phone (bluetooth, actions)); - profiles.insert ("desktop", new Desktop (bluetooth, actions)); - } + profiles = new HashTable (str_hash, str_equal); + profiles.insert ("phone", new Phone (bluetooth, actions)); + profiles.insert ("desktop", new Desktop (bluetooth, actions)); + } - public int run () - { - if (this.loop != null) - { - warning ("service is already running"); - return 1; - } + public int run () + { + if (this.loop != null) + { + warning ("service is already running"); + return Posix.EXIT_FAILURE; + } - Bus.own_name (BusType.SESSION, - "com.canonical.indicator.bluetooth", - BusNameOwnerFlags.NONE, - this.on_bus_acquired, - null, - this.on_name_lost); + Bus.own_name (BusType.SESSION, + "com.canonical.indicator.bluetooth", + BusNameOwnerFlags.NONE, + this.on_bus_acquired, + null, + this.on_name_lost); - this.loop = new MainLoop (null, false); - this.loop.run (); - return 0; - } + this.loop = new MainLoop (null, false); + this.loop.run (); + return Posix.EXIT_SUCCESS; + } - void on_bus_acquired (DBusConnection connection, string name) - { - debug (@"bus acquired: $name"); + void on_bus_acquired (DBusConnection connection, string name) + { + debug (@"bus acquired: $name"); - var object_path = "/com/canonical/indicator/bluetooth"; - try - { - connection.export_action_group (object_path, this.actions); - } - catch (Error e) - { - critical (@"Unable to export actions on $object_path: $(e.message)"); - } + var object_path = "/com/canonical/indicator/bluetooth"; + try + { + connection.export_action_group (object_path, this.actions); + } + catch (Error e) + { + critical (@"Unable to export actions on $object_path: $(e.message)"); + } - this.profiles.for_each ((name,profile) => { - var path = @"/com/canonical/indicator/bluetooth/$name"; - debug (@"exporting menu '$path'"); - profile.export_menu (connection, path); - }); - } + this.profiles.for_each ((name,profile) => { + var path = @"$object_path/$name"; + debug (@"exporting menu '$path'"); + profile.export_menu (connection, path); + }); + } - void on_name_lost (DBusConnection connection, string name) - { - debug (@"name lost: $name"); - this.loop.quit (); - } + void on_name_lost (DBusConnection connection, string name) + { + debug (@"name lost: $name"); + this.loop.quit (); + } } -- cgit v1.2.3 From 90a5d3f5b933320ba6426bbd18198e4c54bb465e Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 5 Aug 2013 19:43:18 -0500 Subject: main() should return service.run() --- src/main.vala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src') diff --git a/src/main.vala b/src/main.vala index fdd08e8..caee3b3 100644 --- a/src/main.vala +++ b/src/main.vala @@ -30,7 +30,5 @@ public static int main (string[] args) // start the service var service = new Service (bluetooth); - service.run (); - - return Posix.EXIT_SUCCESS; + return service.run (); } -- cgit v1.2.3 From 6f6674a851fcce75e1808dd6bec7e36ee319085c Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 5 Aug 2013 19:56:44 -0500 Subject: Remove dbusmenu, gtk, indicator dependencies. Only require gnome-bluetooth on the desktop. --- src/Makefile.am | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 87f4231..2fc41bb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,17 +20,17 @@ indicator_bluetooth_service_VALAFLAGS = \ --pkg rfkill \ --pkg posix \ --pkg glib-2.0 \ - --pkg gtk+-3.0 + --pkg gio-2.0 # -w to disable warnings for vala-generated code indicator_bluetooth_service_CFLAGS = \ -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \ -DLOCALE_DIR=\"$(datadir)/locale\" \ - -w \ - $(INDICATOR_BLUETOOTH_SERVICE_CFLAGS) + -w \ + $(SERVICE_DEPS_CFLAGS) indicator_bluetooth_service_LDADD = \ - $(INDICATOR_BLUETOOTH_SERVICE_LIBS) + $(SERVICE_DEPS_LIBS) CLEANFILES = \ $(patsubst %.vala,%.c,$(filter %.vala, $(SOURCES))) \ -- cgit v1.2.3 From afafeed15465e614e45e3f401b2ef5d88b92aef1 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 6 Aug 2013 10:04:50 -0500 Subject: add device.vala to the repo --- src/device.vala | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 src/device.vala (limited to 'src') diff --git a/src/device.vala b/src/device.vala new file mode 100644 index 0000000..2d665b8 --- /dev/null +++ b/src/device.vala @@ -0,0 +1,156 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +public class Device: Object +{ + public enum Type + { + OTHER, + COMPUTER, + PHONE, + MODEM, + NETWORK, + HEADSET, + HEADPHONES, + VIDEO, + OTHER_AUDIO, + JOYPAD, + KEYPAD, + KEYBOARD, + TABLET, + MOUSE, + PRINTER, + CAMERA + } + + public Type device_type { get; construct; } + public uint id { get; construct; } + public string name { get; construct; } + public string address { get; construct; } + public Icon icon { get; construct; } + public bool is_connectable { get; construct; } + public bool is_connected { get; construct; } + public bool supports_browsing { get; construct; } + public bool supports_file_transfer { get; construct; } + public string print() { + return @"{id:$id, name:$name, address:$address, icon:$(icon.to_string()), device_type:$device_type, is_connectable:$is_connectable, is_connected:$is_connected, supports_browsing:$supports_browsing, supports_file_transfer:$supports_file_transfer}"; + } + + public Device (uint id, + Type device_type, + string name, + string address, + Icon icon, + bool is_connectable, + bool is_connected, + bool supports_browsing, + bool supports_file_transfer) + { + Object (id: id, + device_type: device_type, + name: name, + address: address, + icon: icon, + is_connectable: is_connectable, + is_connected: is_connected, + supports_browsing: supports_browsing, + supports_file_transfer: supports_file_transfer); + } + + public static Type class_to_device_type (uint32 c) + { + switch ((c & 0x1f00) >> 8) + { + case 0x01: + return Type.COMPUTER; + + case 0x02: + switch ((c & 0xfc) >> 2) + { + case 0x01: + case 0x02: + case 0x03: + case 0x05: + return Type.PHONE; + + case 0x04: + return Type.MODEM; + } + break; + + case 0x03: + return Type.NETWORK; + + case 0x04: + switch ((c & 0xfc) >> 2) + { + case 0x01: + case 0x02: + return Type.HEADSET; + + case 0x06: + return Type.HEADPHONES; + + case 0x0b: // vcr + case 0x0c: // video camera + case 0x0d: // camcorder + return Type.VIDEO; + + default: + return Type.OTHER_AUDIO; + } + + case 0x05: + switch ((c & 0xc0) >> 6) + { + case 0x00: + switch ((c & 0x1e) >> 2) + { + case 0x01: + case 0x02: + return Type.JOYPAD; + } + break; + + case 0x01: + return Type.KEYBOARD; + + case 0x02: + switch ((c & 0x1e) >> 2) + { + case 0x05: + return Type.TABLET; + + default: + return Type.MOUSE; + } + } + break; + + case 0x06: + if ((c & 0x80) != 0) + return Type.PRINTER; + if ((c & 0x20) != 0) + return Type.CAMERA; + break; + } + + return 0; + } +} -- cgit v1.2.3 From cd197748b97a3c2aeb4d3470805d3e12b7b46983 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 6 Aug 2013 13:08:06 -0500 Subject: copyediting: don't use 'this.' when it's not needed --- src/profile.vala | 4 ++-- src/service.vala | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/profile.vala b/src/profile.vala index 74c8242..91a0f8e 100644 --- a/src/profile.vala +++ b/src/profile.vala @@ -45,7 +45,7 @@ class Profile: Object try { debug (@"exporting '$profile_name' on $object_path"); - connection.export_menu_model (object_path, this.root); + connection.export_menu_model (object_path, root); } catch (Error e) { @@ -101,7 +101,7 @@ class Profile: Object { root_action = new SimpleAction.stateful (@"root-$profile", null, action_state_for_root()); - this.notify["visible"].connect (() => update_root_action_state()); + notify["visible"].connect (() => update_root_action_state()); } return root_action; diff --git a/src/service.vala b/src/service.vala index 767343f..c51e58f 100644 --- a/src/service.vala +++ b/src/service.vala @@ -35,7 +35,7 @@ public class Service: Object public int run () { - if (this.loop != null) + if (loop != null) { warning ("service is already running"); return Posix.EXIT_FAILURE; @@ -44,12 +44,12 @@ public class Service: Object Bus.own_name (BusType.SESSION, "com.canonical.indicator.bluetooth", BusNameOwnerFlags.NONE, - this.on_bus_acquired, + on_bus_acquired, null, - this.on_name_lost); + on_name_lost); - this.loop = new MainLoop (null, false); - this.loop.run (); + loop = new MainLoop (null, false); + loop.run (); return Posix.EXIT_SUCCESS; } @@ -60,14 +60,14 @@ public class Service: Object var object_path = "/com/canonical/indicator/bluetooth"; try { - connection.export_action_group (object_path, this.actions); + connection.export_action_group (object_path, actions); } catch (Error e) { critical (@"Unable to export actions on $object_path: $(e.message)"); } - this.profiles.for_each ((name,profile) => { + profiles.for_each ((name,profile) => { var path = @"$object_path/$name"; debug (@"exporting menu '$path'"); profile.export_menu (connection, path); @@ -77,6 +77,6 @@ public class Service: Object void on_name_lost (DBusConnection connection, string name) { debug (@"name lost: $name"); - this.loop.quit (); + loop.quit (); } } -- cgit v1.2.3 From c920d6f9c8d967d7de311fc417fd5a8c8e85dd43 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 6 Aug 2013 13:33:08 -0500 Subject: add some extra comments --- src/killswitch.vala | 9 +++++++-- src/service.vala | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/killswitch.vala b/src/killswitch.vala index 7695c0c..6d8f195 100644 --- a/src/killswitch.vala +++ b/src/killswitch.vala @@ -22,12 +22,14 @@ * either by software (e.g., a session configuration setting) * or by hardware (e.g., user disabled it via a physical switch on her laptop). * - * KillSwitchBluetooth uses this as a backend for its Bluetooth.blocked property. + * KillSwitchBluetooth uses this as the impl for its Bluetooth.blocked property */ public interface KillSwitch: Object { public abstract bool blocked { get; protected set; } + /* Try to block/unblock bluetooth. + * This can fail if the requested state is overruled by a hardware block. */ public abstract void try_set_blocked (bool blocked); } @@ -42,13 +44,16 @@ public class RfKillSwitch: KillSwitch, Object { return_if_fail (this.blocked != blocked); - // try to soft-block all the bluetooth devices + // Try to soft-block all the bluetooth devices var event = Linux.RfKillEvent() { op = Linux.RfKillOp.CHANGE_ALL, type = Linux.RfKillType.BLUETOOTH, soft = (uint8)blocked }; + /* Write this request to rfkill. + * Don't update this object's "blocked" property here -- + * We'll get the update when on_channel_event() reads it below */ var bwritten = Posix.write (fd, &event, sizeof(Linux.RfKillEvent)); if (bwritten == -1) warning (@"Could not write rfkill event: $(strerror(errno))"); diff --git a/src/service.vala b/src/service.vala index c51e58f..e6d217a 100644 --- a/src/service.vala +++ b/src/service.vala @@ -18,6 +18,10 @@ * Robert Ancell */ +/** + * Boilerplate class to own the name on the bus, + * to create the profiles, and to export them on the bus. + */ public class Service: Object { private MainLoop loop; -- cgit v1.2.3 From bbca3ae3b9bd150902cebd938df24f1ad4ed7d3d Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 6 Aug 2013 13:43:31 -0500 Subject: copyediting: fix lines that wrap --- src/bluez.vala | 38 +++++++++++++++++++++------------ src/desktop.vala | 64 ++++++++++++++++++++++++++++++++++++++++---------------- src/phone.vala | 8 +++++-- src/profile.vala | 31 ++++++++++++++++++++------- 4 files changed, 101 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/bluez.vala b/src/bluez.vala index d84d869..42166fb 100644 --- a/src/bluez.vala +++ b/src/bluez.vala @@ -55,9 +55,10 @@ public class Bluez: KillswitchBluetooth { manager = Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", "/"); - // get the current default adapter, and watch for future default adapters + // get the current default adapter & watch for future default adapters adapter_path = manager.default_adapter (); - manager.default_adapter_changed.connect ((object_path) => on_default_adapter_changed (object_path)); + manager.default_adapter_changed.connect ((object_path) + => on_default_adapter_changed (object_path)); } catch (Error e) { @@ -72,8 +73,12 @@ public class Bluez: KillswitchBluetooth if (object_path != null) try { debug (@"using default adapter at $object_path"); - default_adapter = Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", object_path); - default_adapter.property_changed.connect(() => on_default_adapter_properties_changed()); + default_adapter = Bus.get_proxy_sync (BusType.SYSTEM, + "org.bluez", + object_path); + + default_adapter.property_changed.connect(() + => on_default_adapter_properties_changed()); default_adapter.device_removed.connect((adapter, path) => { var id = path_to_id.lookup (path); @@ -83,7 +88,9 @@ public class Bluez: KillswitchBluetooth devices_changed (); }); - default_adapter.device_created.connect((adapter, path) => add_device (path)); + default_adapter.device_created.connect((adapter, path) + => add_device (path)); + foreach (string device_path in default_adapter.list_devices()) add_device (device_path); } @@ -136,7 +143,7 @@ public class Bluez: KillswitchBluetooth return uuid16; } - /* A device supports file transfer if OBEXObjectPush is in its uuid list */ + // A device supports file transfer if OBEXObjectPush is in its uuid list private bool device_supports_file_transfer (uint16[] uuids) { foreach (uint16 uuid16 in uuids) @@ -146,7 +153,7 @@ public class Bluez: KillswitchBluetooth return false; } - /* A device supports browsing if OBEXFileTransfer is in its uuid list */ + // A device supports browsing if OBEXFileTransfer is in its uuid list private bool device_supports_browsing (uint16[] uuids) { foreach (uint16 uuid16 in uuids) @@ -205,7 +212,8 @@ public class Bluez: KillswitchBluetooth } // call "Connect" on the specified interface - private void device_connect_on_interface (DBusProxy proxy, string interface_name) + private void device_connect_on_interface (DBusProxy proxy, + string interface_name) { var bus = proxy.get_connection (); var object_path = proxy.get_object_path (); @@ -219,7 +227,7 @@ public class Bluez: KillswitchBluetooth } catch (Error e) { - debug (@"Unable to call $interface_name.Connect() on $object_path: $(e.message)"); + debug (@"$object_path $interface_name.Connect() failed: $(e.message)"); } } @@ -255,7 +263,9 @@ public class Bluez: KillswitchBluetooth { try { - org.bluez.Device device = Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", object_path); + org.bluez.Device device = Bus.get_proxy_sync (BusType.SYSTEM, + "org.bluez", + object_path); path_to_proxy.insert (object_path, device); device.property_changed.connect(() => update_device (device)); update_device (device); @@ -358,10 +368,12 @@ public class Bluez: KillswitchBluetooth public override void set_device_connected (uint id, bool connected) { var device = id_to_device.lookup (id); - var object_path = id_to_path.lookup (id); - var proxy = (object_path != null) ? path_to_proxy.lookup (object_path) : null; + var path = id_to_path.lookup (id); + var proxy = (path != null) ? path_to_proxy.lookup (path) : null; - if ((proxy != null) && (device != null) && (device.is_connected != connected)) + if ((proxy != null) + && (device != null) + && (device.is_connected != connected)) { if (connected) device_connect (proxy); diff --git a/src/desktop.vala b/src/desktop.vala index 24d7f9f..8f4a9c8 100644 --- a/src/desktop.vala +++ b/src/desktop.vala @@ -128,30 +128,35 @@ class Desktop: Profile submenu.append_item (create_device_connection_menuitem (device)); if (device.supports_browsing) - submenu.append (_("Browse files…"), @"indicator.desktop-browse-files::$(device.address)"); + submenu.append (_("Browse files…"), + @"indicator.desktop-browse-files::$(device.address)"); if (device.supports_file_transfer) - submenu.append (_("Send files…"), @"indicator.desktop-send-file::$(device.address)"); + submenu.append (_("Send files…"), + @"indicator.desktop-send-file::$(device.address)"); switch (device.device_type) { case Device.Type.KEYBOARD: - submenu.append (_("Keyboard Settings…"), "indicator.desktop-show-settings::keyboard"); + submenu.append (_("Keyboard Settings…"), + "indicator.desktop-show-settings::keyboard"); break; case Device.Type.MOUSE: case Device.Type.TABLET: - submenu.append (_("Mouse and Touchpad Settings…"), "indicator.desktop-show-settings::mouse"); + submenu.append (_("Mouse and Touchpad Settings…"), + "indicator.desktop-show-settings::mouse"); break; case Device.Type.HEADSET: case Device.Type.HEADPHONES: case Device.Type.OTHER_AUDIO: - submenu.append (_("Sound Settings…"), "indicator.desktop-show-settings::sound"); + submenu.append (_("Sound Settings…"), + "indicator.desktop-show-settings::sound"); break; } - /* only show the device if it's got actions that we can perform on it */ + // only show the device if it's got actions that we can perform on it if (submenu.get_n_items () > 0) { item = new MenuItem (device.name, null); @@ -171,7 +176,8 @@ class Desktop: Profile section = new Menu (); section.append_item (create_enabled_menuitem ()); item = new MenuItem ("Visible", "indicator.desktop-discoverable"); - item.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.switch"); + item.set_attribute ("x-canonical-type", "s", + "com.canonical.indicator.switch"); section.append_item (item); menu.append_section (null, section); @@ -182,8 +188,10 @@ class Desktop: Profile // settings section section = new Menu (); - section.append (_("Set Up New Device…"), "indicator.desktop-wizard"); - section.append (_("Bluetooth Settings…"), "indicator.desktop-show-settings::bluetooth"); + section.append (_("Set Up New Device…"), + "indicator.desktop-wizard"); + section.append (_("Bluetooth Settings…"), + "indicator.desktop-show-settings::bluetooth"); menu.append_section (null, section); } @@ -198,19 +206,32 @@ class Desktop: Profile Action create_discoverable_action (Bluetooth bluetooth) { - var action = new SimpleAction.stateful ("desktop-discoverable", null, bluetooth.discoverable); + var action = new SimpleAction.stateful ("desktop-discoverable", + null, + bluetooth.discoverable); + + action.activate.connect (() + => action.set_state (!action.get_state().get_boolean())); + + action.notify["state"].connect (() + => bluetooth.try_set_discoverable (action.get_state().get_boolean())); + + bluetooth.notify["discoverable"].connect (() + => action.set_state (bluetooth.discoverable)); + action.set_enabled (bluetooth.powered); - action.activate.connect (() => action.set_state (!action.get_state().get_boolean())); - action.notify["state"].connect (() => bluetooth.try_set_discoverable (action.get_state().get_boolean())); - bluetooth.notify["discoverable"].connect (() => action.set_state (bluetooth.discoverable)); - bluetooth.notify["powered"].connect (() => action.set_enabled (bluetooth.powered)); + bluetooth.notify["powered"].connect (() + => action.set_enabled (bluetooth.powered)); + return action; } Action create_wizard_action () { var action = new SimpleAction ("desktop-wizard", null); - action.activate.connect (() => spawn_command_line_async ("bluetooth-wizard")); + + action.activate.connect (() + => spawn_command_line_async ("bluetooth-wizard")); return action; } @@ -220,7 +241,8 @@ class Desktop: Profile action.activate.connect ((action, address) => { var uri = @"obex://[$(address.get_string())]/"; var file = File.new_for_uri (uri); - file.mount_enclosing_volume.begin (MountMountFlags.NONE, null, null, (obj, res) => { + file.mount_enclosing_volume.begin (MountMountFlags.NONE, + null, null, (obj, res) => { try { AppInfo.launch_default_for_uri (uri, null); } catch (Error e) { @@ -234,16 +256,22 @@ class Desktop: Profile Action create_send_file_action () { var action = new SimpleAction ("desktop-send-file", VariantType.STRING); + action.activate.connect ((action, address) => { - spawn_command_line_async ("bluetooth-sendto --device=$(address.get_string())"); + var cmd = @"bluetooth-sendto --device=$(address.get_string())"; + spawn_command_line_async (cmd); }); + return action; } Action create_show_settings_action () { var action = new SimpleAction ("desktop-show-settings", VariantType.STRING); - action.activate.connect ((action, panel) => show_settings (panel.get_string())); + + action.activate.connect ((action, panel) + => show_settings (panel.get_string())); + return action; } } diff --git a/src/phone.vala b/src/phone.vala index 05e6b9d..8adc111 100644 --- a/src/phone.vala +++ b/src/phone.vala @@ -39,7 +39,8 @@ class Phone: Profile var section = new Menu (); section.append_item (create_enabled_menuitem ()); - section.append (_("Bluetooth settings…"), "indicator.phone-show-settings::bluetooth"); + section.append (_("Bluetooth settings…"), + "indicator.phone-show-settings::bluetooth"); menu.append_section (null, section); // know when to show the indicator & when to hide it @@ -66,7 +67,10 @@ class Phone: Profile Action create_settings_action () { var action = new SimpleAction ("phone-show-settings", VariantType.STRING); - action.activate.connect ((action, panel) => show_settings (panel.get_string())); + + action.activate.connect ((action, panel) + => show_settings (panel.get_string())); + return action; } } diff --git a/src/profile.vala b/src/profile.vala index 91a0f8e..03c7b8b 100644 --- a/src/profile.vala +++ b/src/profile.vala @@ -69,14 +69,20 @@ class Profile: Object protected MenuItem create_enabled_menuitem () { MenuItem item = new MenuItem ("Bluetooth", "indicator.bluetooth-enabled"); - item.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.switch"); + + item.set_attribute ("x-canonical-type", "s", + "com.canonical.indicator.switch"); + return item; } private MenuItem create_root_menuitem () { var item = new MenuItem (null, @"indicator.root-$profile_name"); - item.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.root"); + + item.set_attribute ("x-canonical-type", "s", + "com.canonical.indicator.root"); + return item; } @@ -86,10 +92,19 @@ class Profile: Object protected Action create_enabled_action (Bluetooth bluetooth) { - var action = new SimpleAction.stateful ("bluetooth-enabled", null, !bluetooth.blocked); - action.activate.connect (() => action.set_state (!action.get_state().get_boolean())); - action.notify["state"].connect (() => bluetooth.try_set_blocked (!action.get_state().get_boolean())); - bluetooth.notify["blocked"].connect (() => action.set_state (!bluetooth.blocked)); + var action = new SimpleAction.stateful ("bluetooth-enabled", + null, + !bluetooth.blocked); + + action.activate.connect (() + => action.set_state (!action.get_state().get_boolean())); + + action.notify["state"].connect (() + => bluetooth.try_set_blocked (!action.get_state().get_boolean())); + + bluetooth.notify["blocked"].connect (() + => action.set_state (!bluetooth.blocked)); + return action; } @@ -99,7 +114,9 @@ class Profile: Object { if (root_action == null) { - root_action = new SimpleAction.stateful (@"root-$profile", null, action_state_for_root()); + root_action = new SimpleAction.stateful (@"root-$profile", + null, + action_state_for_root()); notify["visible"].connect (() => update_root_action_state()); } -- cgit v1.2.3 From df2bb3084af551770e9cd3f3e9b4cf59c439041b Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 6 Aug 2013 15:21:44 -0500 Subject: templated strings need to begin with a '@' --- src/desktop.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/desktop.vala b/src/desktop.vala index 8f4a9c8..e4e7d04 100644 --- a/src/desktop.vala +++ b/src/desktop.vala @@ -246,7 +246,7 @@ class Desktop: Profile try { AppInfo.launch_default_for_uri (uri, null); } catch (Error e) { - warning ("unable to launch '$uri': $(e.message)"); + warning (@"unable to launch '$uri': $(e.message)"); } }); }); -- cgit v1.2.3 From b82c9a9de54e3659386a178926dc8ce99917c6f6 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 6 Aug 2013 15:33:26 -0500 Subject: copyediting: whitespace, type inference --- src/bluez.vala | 40 ++++++++++++++++++---------------------- src/desktop.vala | 27 ++++++++++++++++++--------- src/killswitch.vala | 2 +- src/profile.vala | 6 +++--- src/service.vala | 2 +- 5 files changed, 41 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/src/bluez.vala b/src/bluez.vala index 42166fb..9baacd5 100644 --- a/src/bluez.vala +++ b/src/bluez.vala @@ -77,10 +77,10 @@ public class Bluez: KillswitchBluetooth "org.bluez", object_path); - default_adapter.property_changed.connect(() - => on_default_adapter_properties_changed()); + default_adapter.property_changed.connect (() + => on_default_adapter_properties_changed ()); - default_adapter.device_removed.connect((adapter, path) => { + default_adapter.device_removed.connect ((adapter, path) => { var id = path_to_id.lookup (path); path_to_id.remove (path); id_to_path.remove (id); @@ -88,10 +88,10 @@ public class Bluez: KillswitchBluetooth devices_changed (); }); - default_adapter.device_created.connect((adapter, path) + default_adapter.device_created.connect ((adapter, path) => add_device (path)); - foreach (string device_path in default_adapter.list_devices()) + foreach (var device_path in default_adapter.list_devices ()) add_device (device_path); } catch (Error e) @@ -109,12 +109,12 @@ public class Bluez: KillswitchBluetooth if (default_adapter != null) try { - var properties = default_adapter.get_properties(); + var properties = default_adapter.get_properties (); - var v = properties.lookup("Discoverable"); + var v = properties.lookup ("Discoverable"); is_discoverable = (v != null) && v.get_boolean (); - v = properties.lookup("Powered"); + v = properties.lookup ("Powered"); is_powered = (v != null) && v.get_boolean (); } catch (Error e) @@ -146,7 +146,7 @@ public class Bluez: KillswitchBluetooth // A device supports file transfer if OBEXObjectPush is in its uuid list private bool device_supports_file_transfer (uint16[] uuids) { - foreach (uint16 uuid16 in uuids) + foreach (var uuid16 in uuids) if (uuid16 == 0x1105) // OBEXObjectPush return true; @@ -156,7 +156,7 @@ public class Bluez: KillswitchBluetooth // A device supports browsing if OBEXFileTransfer is in its uuid list private bool device_supports_browsing (uint16[] uuids) { - foreach (uint16 uuid16 in uuids) + foreach (var uuid16 in uuids) if (uuid16 == 0x1106) // OBEXFileTransfer return true; @@ -182,11 +182,11 @@ public class Bluez: KillswitchBluetooth if ((intro != null) && (intro.n_children() > 0)) { - string xml = intro.get_child_value(0).get_string(); + var xml = intro.get_child_value(0).get_string(); var info = new DBusNodeInfo.for_xml (xml); if (info != null) { - foreach (DBusInterfaceInfo i in info.interfaces) + foreach (var i in info.interfaces) { if ((i.name == "org.bluez.AudioSink") || (i.name == "org.bluez.Headset") || @@ -317,32 +317,28 @@ public class Bluez: KillswitchBluetooth v = properties.lookup ("Alias"); if (v == null) v = properties.lookup ("Name"); - string name = v == null ? _("Unknown") : v.get_string (); + var name = v == null ? _("Unknown") : v.get_string (); // look up the device's bus address v = properties.lookup ("Address"); - string address = v.get_string (); + var address = v.get_string (); // look up the device's bus address - Icon icon; v = properties.lookup ("Icon"); - if (v == null) - icon = new ThemedIcon ("unknown"); - else - icon = new ThemedIcon (v.get_string()); + var icon = new ThemedIcon (v != null ? v.get_string() : "unknown"); // derive a Connectable flag for this device var is_connectable = device_is_connectable (device_proxy as DBusProxy); // look up the device's Connected flag v = properties.lookup ("Connected"); - bool is_connected = (v != null) && v.get_boolean (); + var is_connected = (v != null) && v.get_boolean (); // derive the uuid-related attributes we care about v = properties.lookup ("UUIDs"); string[] uuid_strings = v.dup_strv (); uint16[] uuids = {}; - foreach (string s in uuid_strings) + foreach (var s in uuid_strings) uuids += get_uuid16_from_uuid_string (s); var supports_browsing = device_supports_browsing (uuids); var supports_file_transfer = device_supports_file_transfer (uuids); @@ -386,7 +382,7 @@ public class Bluez: KillswitchBluetooth { if (discoverable != b) try { - default_adapter.set_property ("Discoverable", new Variant.boolean(b)); + default_adapter.set_property ("Discoverable", new Variant.boolean (b)); } catch (Error e) { diff --git a/src/desktop.vala b/src/desktop.vala index e4e7d04..588a584 100644 --- a/src/desktop.vala +++ b/src/desktop.vala @@ -72,7 +72,7 @@ class Desktop: Profile bluetooth.devices_changed.connect (()=> { if (idle_rebuild_id == 0) idle_rebuild_id = Idle.add (() => { - rebuild_device_section(); + rebuild_device_section (); idle_rebuild_id = 0; return false; }); @@ -90,20 +90,29 @@ class Desktop: Profile MenuItem create_device_connection_menuitem (Device device) { - var action_name = @"desktop-device-$(device.id)-connected"; + var id = device.id; + var action_name = @"desktop-device-$(id)-connected"; var item = new MenuItem (_("Connection"), @"indicator.$action_name"); - item.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.switch"); + item.set_attribute ("x-canonical-type", + "s", "com.canonical.indicator.switch"); // if this doesn't already have an action, create one - if (!connect_actions.contains (device.id)) + if (!connect_actions.contains (id)) { debug (@"creating action for $action_name"); - var action = new SimpleAction.stateful (action_name, null, device.is_connected); - action.activate.connect (() => action.set_state (!action.get_state().get_boolean())); - action.notify["state"].connect (() => bluetooth.set_device_connected (device.id, action.get_state().get_boolean())); - connect_actions.insert (device.id, action); - action_group.insert (action); + var a = new SimpleAction.stateful (action_name, + null, + device.is_connected); + + a.activate.connect (() + => a.set_state (!a.get_state().get_boolean())); + + a.notify["state"].connect (() + => bluetooth.set_device_connected (id, a.get_state().get_boolean())); + + connect_actions.insert (device.id, a); + action_group.insert (a); } else { diff --git a/src/killswitch.vala b/src/killswitch.vala index 6d8f195..08ee0cc 100644 --- a/src/killswitch.vala +++ b/src/killswitch.vala @@ -159,7 +159,7 @@ public class RfKillSwitch: KillSwitch, Object /* update our blocked property. it should be true if any bluetooth entry is hard- or soft-blocked */ var b = false; - foreach (Entry entry in entries.get_values()) + foreach (var entry in entries.get_values ()) if ((b = (entry.soft || entry.hard))) break; blocked = b; diff --git a/src/profile.vala b/src/profile.vala index 03c7b8b..96d1b5c 100644 --- a/src/profile.vala +++ b/src/profile.vala @@ -68,7 +68,7 @@ class Profile: Object protected MenuItem create_enabled_menuitem () { - MenuItem item = new MenuItem ("Bluetooth", "indicator.bluetooth-enabled"); + var item = new MenuItem ("Bluetooth", "indicator.bluetooth-enabled"); item.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.switch"); @@ -131,8 +131,8 @@ class Profile: Object protected Variant action_state_for_root () { - bool blocked = bluetooth.blocked; - bool powered = bluetooth.powered; + var blocked = bluetooth.blocked; + var powered = bluetooth.powered; string a11y; string icon_name; diff --git a/src/service.vala b/src/service.vala index e6d217a..0c18b36 100644 --- a/src/service.vala +++ b/src/service.vala @@ -71,7 +71,7 @@ public class Service: Object critical (@"Unable to export actions on $object_path: $(e.message)"); } - profiles.for_each ((name,profile) => { + profiles.for_each ((name, profile) => { var path = @"$object_path/$name"; debug (@"exporting menu '$path'"); profile.export_menu (connection, path); -- cgit v1.2.3 From c1d8ca09d39ba3070d22529bd40f564e7647bfb3 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 9 Aug 2013 18:18:59 -0500 Subject: on shutdown, unexport the menus and unown the bus name --- src/profile.vala | 15 +++++++++++++-- src/service.vala | 50 ++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 49 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/profile.vala b/src/profile.vala index 96d1b5c..f89490b 100644 --- a/src/profile.vala +++ b/src/profile.vala @@ -23,6 +23,7 @@ class Profile: Object protected string profile_name; protected Menu root; protected Menu menu; + private uint menu_export_id; protected bool visible { get; set; default = true; } @@ -44,8 +45,8 @@ class Profile: Object { try { - debug (@"exporting '$profile_name' on $object_path"); - connection.export_menu_model (object_path, root); + debug (@"exporting menu '$profile_name'"); + menu_export_id = connection.export_menu_model (object_path, root); } catch (Error e) { @@ -53,6 +54,16 @@ class Profile: Object } } + public void unexport_menu (DBusConnection connection) + { + if (menu_export_id != 0) + { + debug (@"unexporting menu '$profile_name'"); + connection.unexport_menu_model (menu_export_id); + menu_export_id = 0; + } + } + protected void spawn_command_line_async (string command) { try { diff --git a/src/service.vala b/src/service.vala index 0c18b36..20fdd1e 100644 --- a/src/service.vala +++ b/src/service.vala @@ -27,6 +27,25 @@ public class Service: Object private MainLoop loop; private SimpleActionGroup actions; private HashTable profiles; + private DBusConnection connection; + private uint exported_action_id; + private const string OBJECT_PATH = "/com/canonical/indicator/bluetooth"; + + private void unexport () + { + if (connection != null) + { + profiles.for_each ((name, profile) + => profile.unexport_menu (connection)); + + if (exported_action_id != 0) + { + debug (@"unexporting action group '$(OBJECT_PATH)'"); + connection.unexport_action_group (exported_action_id); + exported_action_id = 0; + } + } + } public Service (Bluetooth bluetooth) { @@ -45,37 +64,40 @@ public class Service: Object return Posix.EXIT_FAILURE; } - Bus.own_name (BusType.SESSION, - "com.canonical.indicator.bluetooth", - BusNameOwnerFlags.NONE, - on_bus_acquired, - null, - on_name_lost); + var own_name_id = Bus.own_name (BusType.SESSION, + "com.canonical.indicator.bluetooth", + BusNameOwnerFlags.NONE, + on_bus_acquired, + null, + on_name_lost); loop = new MainLoop (null, false); loop.run (); + + // cleanup + unexport (); + Bus.unown_name (own_name_id); return Posix.EXIT_SUCCESS; } void on_bus_acquired (DBusConnection connection, string name) { debug (@"bus acquired: $name"); + this.connection = connection; - var object_path = "/com/canonical/indicator/bluetooth"; try { - connection.export_action_group (object_path, actions); + debug (@"exporting action group '$(OBJECT_PATH)'"); + exported_action_id = connection.export_action_group (OBJECT_PATH, + actions); } catch (Error e) { - critical (@"Unable to export actions on $object_path: $(e.message)"); + critical (@"Unable to export actions on $OBJECT_PATH: $(e.message)"); } - profiles.for_each ((name, profile) => { - var path = @"$object_path/$name"; - debug (@"exporting menu '$path'"); - profile.export_menu (connection, path); - }); + profiles.for_each ((name, profile) + => profile.export_menu (connection, @"$OBJECT_PATH/$name")); } void on_name_lost (DBusConnection connection, string name) -- cgit v1.2.3 From aacf0decdb47a27d05b4701517a0e36e68491f66 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 9 Aug 2013 18:22:31 -0500 Subject: create Profile.root_action in Profile's constructor. --- src/desktop.vala | 2 +- src/phone.vala | 2 +- src/profile.vala | 22 ++++++---------------- 3 files changed, 8 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/desktop.vala b/src/desktop.vala index 588a584..4a10db8 100644 --- a/src/desktop.vala +++ b/src/desktop.vala @@ -51,7 +51,7 @@ class Desktop: Profile // build the static actions Action[] actions = {}; - actions += get_root_action (profile_name); + actions += root_action; actions += create_enabled_action (bluetooth); actions += create_discoverable_action (bluetooth); actions += create_wizard_action (); diff --git a/src/phone.vala b/src/phone.vala index 8adc111..43ca554 100644 --- a/src/phone.vala +++ b/src/phone.vala @@ -31,7 +31,7 @@ class Phone: Profile // build the static actions Action[] actions = {}; - actions += get_root_action (profile_name); + actions += root_action; actions += create_enabled_action (bluetooth); actions += create_settings_action (); foreach (var a in actions) diff --git a/src/profile.vala b/src/profile.vala index f89490b..c288b4e 100644 --- a/src/profile.vala +++ b/src/profile.vala @@ -24,6 +24,7 @@ class Profile: Object protected Menu root; protected Menu menu; private uint menu_export_id; + protected SimpleAction root_action; protected bool visible { get; set; default = true; } @@ -32,6 +33,11 @@ class Profile: Object this.bluetooth = bluetooth; this.profile_name = profile_name; + root_action = new SimpleAction.stateful (@"root-$profile_name", + null, + action_state_for_root()); + notify["visible"].connect (() => update_root_action_state()); + menu = new Menu (); var item = create_root_menuitem (); @@ -119,22 +125,6 @@ class Profile: Object return action; } - protected SimpleAction root_action = null; - - protected SimpleAction get_root_action (string profile) - { - if (root_action == null) - { - root_action = new SimpleAction.stateful (@"root-$profile", - null, - action_state_for_root()); - - notify["visible"].connect (() => update_root_action_state()); - } - - return root_action; - } - protected void update_root_action_state () { root_action.set_state (action_state_for_root ()); -- cgit v1.2.3