diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/bluez.vala | 180 | ||||
-rw-r--r-- | src/gnome-bluetooth-1.0.vapi | 59 | ||||
-rw-r--r-- | src/indicator-bluetooth.vala | 251 |
4 files changed, 218 insertions, 274 deletions
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, ""); |