aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac1
-rw-r--r--src/Makefile.am2
-rw-r--r--src/bluez.vala180
-rw-r--r--src/gnome-bluetooth-1.0.vapi59
-rw-r--r--src/indicator-bluetooth.vala251
5 files changed, 219 insertions, 274 deletions
diff --git a/configure.ac b/configure.ac
index d7d5b4d..eee0f17 100644
--- a/configure.ac
+++ b/configure.ac
@@ -13,6 +13,7 @@ dnl ###########################################################################
PKG_CHECK_MODULES(INDICATOR_BLUETOOTH, [
glib-2.0
gtk+-3.0
+ gnome-bluetooth-1.0
appindicator3-0.1
])
diff --git a/src/Makefile.am b/src/Makefile.am
index 64cb42d..7e4605a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -2,7 +2,7 @@ bin_PROGRAMS = indicator-bluetooth
indicator_bluetooth_SOURCES = \
config.vapi \
- bluez.vala \
+ gnome-bluetooth-1.0.vapi \
indicator-bluetooth.vala \
rfkill.vala
diff --git a/src/bluez.vala b/src/bluez.vala
deleted file mode 100644
index c20acc7..0000000
--- a/src/bluez.vala
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (C) 2012 Canonical Ltd.
- * Author: Robert Ancell <robert.ancell@canonical.com>
- *
- * This program is free software: you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later
- * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
- * license.
- */
-
-public class BluezManager : Object
-{
- public BluezAdapter default_adapter;
-
- public BluezManager ()
- {
- }
-
- public void start () throws IOError
- {
- proxy = Bus.get_proxy_sync<BluezManagerInterface> (BusType.SYSTEM, "org.bluez", "/");
- proxy.default_adapter_changed.connect (default_adapter_changed_cb);
- default_adapter_changed_cb (proxy.default_adapter ());
- }
-
- private BluezManagerInterface proxy;
-
- private void default_adapter_changed_cb (string path)
- {
- default_adapter = new BluezAdapter (path);
- }
-}
-
-public class BluezAdapter : Object
-{
- public List<BluezDevice> get_devices ()
- {
- var devices = new List<BluezDevice> ();
- foreach (var device in _devices)
- devices.append (device);
- return devices;
- }
-
- private bool _discoverable = false;
- public bool discoverable
- {
- get { return _discoverable; }
- set
- {
- _discoverable = value;
- proxy.set_property ("Discoverable", new Variant.boolean (value));
- }
- }
-
- internal string path;
- private List<BluezDevice> _devices;
- private BluezAdapterInterface proxy;
-
- internal BluezAdapter (string path)
- {
- this.path = path;
- _devices = new List<BluezDevice> ();
- proxy = Bus.get_proxy_sync<BluezAdapterInterface> (BusType.SYSTEM, "org.bluez", path);
-
- proxy.property_changed.connect (property_changed_cb);
- var properties = proxy.get_properties ();
- var iter = HashTableIter<string, Variant> (properties);
- string name;
- Variant value;
- while (iter.next (out name, out value))
- property_changed_cb (name, value);
-
- proxy.device_created.connect (device_created_cb);
- foreach (var device_path in proxy.list_devices ())
- device_created_cb (device_path);
- }
-
- private void property_changed_cb (string name, Variant value)
- {
- stderr.printf ("%s %s=%s\n", path, name, value.print (false));
- if (name == "Discoverable" && value.is_of_type (VariantType.BOOLEAN))
- {
- _discoverable = value.get_boolean ();
- notify_property ("discoverable");
- }
- }
-
- private void device_created_cb (string path)
- {
- foreach (var device in _devices)
- if (device.path == path)
- return;
-
- var device = new BluezDevice (path);
- _devices.append (device);
- }
-}
-
-public class BluezDevice : Object
-{
- private string _name = null;
- public string name { get { return _name; } }
-
- private uint32 _class = 0;
- public uint32 class { get { return _class; } }
-
- internal string path;
- private BluezDeviceInterface proxy;
-
- internal BluezDevice (string path)
- {
- this.path = path;
- proxy = Bus.get_proxy_sync<BluezDeviceInterface> (BusType.SYSTEM, "org.bluez", path);
-
- proxy.property_changed.connect (property_changed_cb);
- var properties = proxy.get_properties ();
- var iter = HashTableIter<string, Variant> (properties);
- string name;
- Variant value;
- while (iter.next (out name, out value))
- property_changed_cb (name, value);
-
- //var input_device = Bus.get_proxy_sync<BluezInputInterface> (BusType.SYSTEM, "org.bluez", path);
- //input_device.property_changed.connect (input_property_changed_cb);
- }
-
- private void property_changed_cb (string name, Variant value)
- {
- stderr.printf ("%s %s=%s\n", path, name, value.print (false));
- if (name == "Name" && value.is_of_type (VariantType.STRING))
- _name = value.get_string ();
- if (name == "Class" && value.is_of_type (VariantType.UINT32))
- _class = value.get_uint32 ();
- }
-
- private void input_property_changed_cb (string name, Variant value)
- {
- stderr.printf ("%s i %s=%s\n", path, name, value.print (false));
- }
-}
-
-[DBus (name = "org.bluez.Manager")]
-private interface BluezManagerInterface : Object
-{
- public abstract string default_adapter () throws IOError;
- public signal void default_adapter_changed (string path);
-}
-
-[DBus (name = "org.bluez.Adapter")]
-private interface BluezAdapterInterface : Object
-{
- public abstract string[] list_devices () throws IOError;
- public abstract HashTable<string, Variant> get_properties () throws IOError;
- public abstract void set_property (string name, Variant value) throws IOError;
- public signal void property_changed (string name, Variant value);
- public signal void device_created (string path);
-}
-
-[DBus (name = "org.bluez.Device")]
-private interface BluezDeviceInterface : Object
-{
- public abstract HashTable<string, Variant> get_properties () throws IOError;
- public signal void property_changed (string name, Variant value);
-}
-
-[DBus (name = "org.bluez.Audio")]
-private interface BluezAudioInterface : Object
-{
- public abstract void connect () throws IOError;
-}
-
-[DBus (name = "org.bluez.Input")]
-private interface BluezInputInterface : Object
-{
- public abstract void connect () throws IOError;
- public abstract void disconnect () throws IOError;
- public abstract HashTable<string, Variant> get_properties () throws IOError;
- public signal void property_changed (string name, Variant value);
-}
diff --git a/src/gnome-bluetooth-1.0.vapi b/src/gnome-bluetooth-1.0.vapi
new file mode 100644
index 0000000..ac60bf0
--- /dev/null
+++ b/src/gnome-bluetooth-1.0.vapi
@@ -0,0 +1,59 @@
+[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; }
+}
+
+[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);
+
+}
diff --git a/src/indicator-bluetooth.vala b/src/indicator-bluetooth.vala
index e7ca190..4089d9b 100644
--- a/src/indicator-bluetooth.vala
+++ b/src/indicator-bluetooth.vala
@@ -11,6 +11,7 @@
public class BluetoothIndicator : AppIndicator.Indicator
{
+ private GnomeBluetooth.Client client;
private RFKillManager rfkill;
private Gtk.MenuItem status_item;
private Gtk.MenuItem enable_item;
@@ -18,8 +19,9 @@ public class BluetoothIndicator : AppIndicator.Indicator
private Gtk.CheckMenuItem visible_item;
private Gtk.SeparatorMenuItem devices_separator;
private Gtk.MenuItem devices_item;
- private List<Gtk.MenuItem> device_items;
+ private List<BluetoothMenuItem> device_items;
private Gtk.MenuItem settings_item;
+ private Gtk.Menu menu;
public BluetoothIndicator ()
{
@@ -32,13 +34,11 @@ public class BluetoothIndicator : AppIndicator.Indicator
rfkill.device_changed.connect (update_rfkill);
rfkill.device_deleted.connect (update_rfkill);
- /* Get/control bluetooth status from Bluez */
- var bluez = new BluezManager ();
- bluez.start ();
+ client = new GnomeBluetooth.Client ();
set_status (AppIndicator.IndicatorStatus.ACTIVE);
- var menu = new Gtk.Menu ();
+ menu = new Gtk.Menu ();
set_menu (menu);
status_item = new Gtk.MenuItem ();
@@ -51,9 +51,16 @@ public class BluetoothIndicator : AppIndicator.Indicator
menu.append (enable_item);
visible_item = new Gtk.CheckMenuItem.with_label (_("Visible"));
- visible_item.active = bluez.default_adapter.discoverable;
- bluez.default_adapter.notify["discoverable"].connect (() => { visible_item.active = bluez.default_adapter.discoverable; });
- visible_item.activate.connect (() => { bluez.default_adapter.discoverable = visible_item.active; });
+ bool discoverable;
+ client.get ("default-adapter-discoverable", out discoverable);
+ visible_item.active = discoverable;
+ client.notify["default-adapter-discoverable"].connect (() =>
+ {
+ bool is_discoverable;
+ client.get ("default-adapter-discoverable", out is_discoverable);
+ visible_item.active = is_discoverable;
+ });
+ visible_item.activate.connect (() => { client.set ("default-adapter-discoverable", visible_item.active); });
menu.append (visible_item);
devices_separator = new Gtk.SeparatorMenuItem ();
@@ -64,106 +71,112 @@ public class BluetoothIndicator : AppIndicator.Indicator
devices_item.visible = true;
menu.append (devices_item);
- device_items = new List<Gtk.MenuItem> ();
+ device_items = new List<BluetoothMenuItem> ();
- var devices = bluez.default_adapter.get_devices ();
- foreach (var device in devices)
+ 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;
+ if (client.model.get_iter_first (out iter))
{
- var item = new Gtk.MenuItem.with_label (device.name);
- device_items.append (item);
- menu.append (item);
+ do
+ {
+ device_changed_cb (null, iter);
+ } while (client.model.iter_next (ref iter));
+ }
- item.submenu = new Gtk.Menu ();
+ var sep = new Gtk.SeparatorMenuItem ();
+ sep.visible = true;
+ menu.append (sep);
- /* Scan class mask to determine what type of device it is */
- var is_keyboard = false;
- var is_pointer = false;
- var is_audio = false;
- switch ((device.class & 0x1f00) >> 8)
- {
- case 0x04:
- switch ((device.class & 0xfc) >> 2)
- {
- case 0x0b:
- case 0x0c:
- case 0x0d:
- /* (video devices) */
- break;
- default:
- is_audio = true;
- break;
- }
- break;
- case 0x05:
- switch ((device.class & 0xc0) >> 6)
- {
- case 0x00:
- /* (joypads) */
- break;
- case 0x01:
- is_keyboard = true;
- break;
- case 0x02:
- is_pointer = true;
- break;
- }
- break;
- }
+ settings_item = new Gtk.MenuItem.with_label (_("Bluetooth Settings..."));
+ settings_item.activate.connect (() => { show_control_center ("bluetooth"); });
+ settings_item.visible = true;
+ menu.append (settings_item);
- // FIXME: Check by looking at the UUIDs
- var can_receive_files = true;
- var can_browse_files = true;
+ update_rfkill ();
+ }
- if (can_receive_files)
- {
- var i = new Gtk.MenuItem.with_label (_("Send files..."));
- i.visible = true;
- i.activate.connect (() => { Process.spawn_command_line_async ("bluetooth-sendto --device=DEVICE --name=NAME"); }); // FIXME
- item.submenu.append (i);
- }
- if (can_browse_files)
- {
- var i = new Gtk.MenuItem.with_label (_("Browse files..."));
- i.visible = true;
- i.activate.connect (() => { Process.spawn_command_line_async ("gnome-open obex://[%s]/"); }); // FIXME
- item.submenu.append (i);
- }
+ private BluetoothMenuItem? find_menu_item (DBusProxy proxy)
+ {
+ foreach (var item in device_items)
+ if (item.proxy == proxy)
+ return item;
- if (is_keyboard)
- {
- var i = new Gtk.MenuItem.with_label (_("Keyboard Settings..."));
- i.visible = true;
- i.activate.connect (() => { Process.spawn_command_line_async ("gnome-control-center keyboard"); });
- item.submenu.append (i);
- }
+ return null;
+ }
- if (is_pointer)
- {
- var i = new Gtk.MenuItem.with_label (_("Mouse and Touchpad Settings..."));
- i.visible = true;
- i.activate.connect (() => { Process.spawn_command_line_async ("gnome-control-center mouse"); });
- item.submenu.append (i);
- }
+ private void device_changed_cb (Gtk.TreePath? path, Gtk.TreeIter iter)
+ {
+ DBusProxy proxy;
+ string address;
+ string alias;
+ string name;
+ GnomeBluetooth.Type type;
+ string[] uuids;
+ client.model.get (iter,
+ GnomeBluetooth.Column.PROXY, out proxy,
+ GnomeBluetooth.Column.ADDRESS, out address,
+ GnomeBluetooth.Column.ALIAS, out alias,
+ GnomeBluetooth.Column.NAME, out name,
+ GnomeBluetooth.Column.TYPE, out type,
+ 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 (proxy);
+ if (item == null)
+ {
+ item = new BluetoothMenuItem (proxy);
+ item.visible = true;
+ var last_item = devices_item;
+ if (device_items != null)
+ last_item = device_items.last ().data;
+ device_items.append (item);
+ menu.insert (item, menu.get_children ().index (last_item) + 1);
+ }
- if (is_audio)
+ var can_send = false;
+ var can_browse = false;
+ if (uuids != null)
+ {
+ for (var i = 0; uuids[i] != null; i++)
{
- var i = new Gtk.MenuItem.with_label (_("Sound Settings..."));
- i.visible = true;
- i.activate.connect (() => { Process.spawn_command_line_async ("gnome-control-center sound"); });
- item.submenu.append (i);
+ if (uuids[i] == "OBEXObjectPush")
+ can_send = true;
+ if (uuids[i] == "OBEXFileTransfer")
+ can_browse = true;
}
}
- var sep = new Gtk.SeparatorMenuItem ();
- sep.visible = true;
- menu.append (sep);
+ item.label = name;
+ item.alias = alias;
+ item.address = address;
+ item.send_item.visible = can_send;
+ item.browse_item.visible = can_browse;
+ item.keyboard_item.visible = type == GnomeBluetooth.Type.KEYBOARD;
+ item.mouse_item.visible = type == GnomeBluetooth.Type.MOUSE || type == GnomeBluetooth.Type.TABLET;
+ item.sound_item.visible = type == GnomeBluetooth.Type.HEADSET || type == GnomeBluetooth.Type.HEADPHONES || type == GnomeBluetooth.Type.OTHER_AUDIO;
+ }
- settings_item = new Gtk.MenuItem.with_label (_("Bluetooth Settings..."));
- settings_item.activate.connect (() => { Process.spawn_command_line_async ("gnome-control-center bluetooth"); });
- settings_item.visible = true;
- menu.append (settings_item);
+ private void device_removed_cb (Gtk.TreePath path)
+ {
+ Gtk.TreeIter iter;
+ if (!client.model.get_iter (out iter, path))
+ return;
- update_rfkill ();
+ DBusProxy proxy;
+ client.model.get (iter, GnomeBluetooth.Column.PROXY, out proxy);
+
+ var item = find_menu_item (proxy);
+ if (item == null)
+ return;
+
+ device_items.remove (item);
+ menu.remove (item);
}
private void update_rfkill ()
@@ -219,6 +232,58 @@ public class BluetoothIndicator : AppIndicator.Indicator
}
}
+private class BluetoothMenuItem : Gtk.MenuItem
+{
+ public DBusProxy proxy;
+ public string alias;
+ public string address;
+ public Gtk.MenuItem send_item;
+ public Gtk.MenuItem browse_item;
+ public Gtk.MenuItem keyboard_item;
+ public Gtk.MenuItem mouse_item;
+ public Gtk.MenuItem sound_item;
+
+ public BluetoothMenuItem (DBusProxy proxy)
+ {
+ this.proxy = proxy;
+ submenu = new Gtk.Menu ();
+ submenu.visible = true;
+
+ send_item = new Gtk.MenuItem.with_label (_("Send files..."));
+ send_item.visible = true;
+ send_item.activate.connect (() => { GnomeBluetooth.send_to_address (address, alias); });
+ submenu.append (send_item);
+
+ browse_item = new Gtk.MenuItem.with_label (_("Browse files..."));
+ browse_item.activate.connect (() => { GnomeBluetooth.browse_address (null, address, Gdk.CURRENT_TIME, null); });
+ submenu.append (browse_item);
+
+ keyboard_item = new Gtk.MenuItem.with_label (_("Keyboard Settings..."));
+ keyboard_item.activate.connect (() => { show_control_center ("keyboard"); });
+ submenu.append (keyboard_item);
+
+ mouse_item = new Gtk.MenuItem.with_label (_("Mouse and Touchpad Settings..."));
+ mouse_item.activate.connect (() => { show_control_center ("mouse"); });
+ submenu.append (mouse_item);
+
+ sound_item = new Gtk.MenuItem.with_label (_("Sound Settings..."));
+ sound_item.activate.connect (() => { show_control_center ("sound"); });
+ submenu.append (sound_item);
+ }
+}
+
+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);
+ }
+}
+
public static int main (string[] args)
{
Intl.setlocale (LocaleCategory.ALL, "");