243 lines
8.9 KiB
Plaintext
243 lines
8.9 KiB
Plaintext
|
|
#! /usr/bin/python3
|
||
|
|
|
||
|
|
from __future__ import print_function
|
||
|
|
from __future__ import division
|
||
|
|
from __future__ import absolute_import
|
||
|
|
from __future__ import unicode_literals
|
||
|
|
|
||
|
|
import dbus
|
||
|
|
from dbus.mainloop.glib import DBusGMainLoop
|
||
|
|
from gi.repository import Pango
|
||
|
|
import os.path
|
||
|
|
import sys
|
||
|
|
import signal
|
||
|
|
|
||
|
|
#support running uninstalled
|
||
|
|
_dirname = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||
|
|
if os.path.exists(os.path.join(_dirname, "CHANGELOG.md")):
|
||
|
|
sys.path.insert(0, _dirname)
|
||
|
|
|
||
|
|
import blueman.bluez as Bluez
|
||
|
|
from blueman.Constants import *
|
||
|
|
from blueman.Functions import *
|
||
|
|
from blueman.main.SignalTracker import SignalTracker
|
||
|
|
|
||
|
|
enable_rgba_colormap()
|
||
|
|
|
||
|
|
import gi
|
||
|
|
gi.require_version("Gtk", "3.0")
|
||
|
|
from gi.repository import Gtk
|
||
|
|
|
||
|
|
DBusGMainLoop(set_as_default=True)
|
||
|
|
|
||
|
|
# Workaround introspection bug, gnome bug 622084
|
||
|
|
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||
|
|
|
||
|
|
class BluemanAdapters:
|
||
|
|
def __init__(self, selected_hci_dev):
|
||
|
|
setup_icon_path()
|
||
|
|
self.bus = dbus.SystemBus()
|
||
|
|
self.bus.watch_name_owner('org.bluez', self.on_dbus_name_owner_change)
|
||
|
|
|
||
|
|
builder = Gtk.Builder()
|
||
|
|
builder.set_translation_domain("blueman")
|
||
|
|
builder.add_from_file(UI_PATH + "/adapters.ui")
|
||
|
|
self.dialog = builder.get_object("dialog")
|
||
|
|
|
||
|
|
check_single_instance("blueman-adapters", lambda time: self.dialog.present_with_time(time))
|
||
|
|
|
||
|
|
check_bluetooth_status(_("Bluetooth needs to be turned on for the adapter manager to work"), lambda: exit())
|
||
|
|
|
||
|
|
self.dialog.props.icon_name = "blueman-device"
|
||
|
|
self.dialog.connect("response", self.on_dialog_response)
|
||
|
|
self.notebook = builder.get_object("notebook")
|
||
|
|
self.tabs = {}
|
||
|
|
|
||
|
|
try:
|
||
|
|
self.signals = SignalTracker()
|
||
|
|
self.manager = Bluez.Manager()
|
||
|
|
self.signals.Handle("bluez", self.manager, self.on_adapter_added, 'AdapterAdded')
|
||
|
|
adapters = self.manager.list_adapters()
|
||
|
|
for adapter in adapters:
|
||
|
|
self.add_to_notebook(adapter)
|
||
|
|
except Exception as e:
|
||
|
|
print(e)
|
||
|
|
self.manager = None
|
||
|
|
#fixme: show error dialog and exit
|
||
|
|
|
||
|
|
#activate a particular tab according to command line option
|
||
|
|
if selected_hci_dev is not None:
|
||
|
|
if selected_hci_dev in self.tabs:
|
||
|
|
hci_dev_num = int(selected_hci_dev[3:])
|
||
|
|
self.notebook.set_current_page(hci_dev_num)
|
||
|
|
else:
|
||
|
|
print('Error: the selected adapter does not exist')
|
||
|
|
self.dialog.show()
|
||
|
|
Gtk.main()
|
||
|
|
|
||
|
|
def on_dialog_response(self, dialog, response_id):
|
||
|
|
for hci, settings in self.tabs.items():
|
||
|
|
if settings['changed']:
|
||
|
|
settings['adapter'].set_name(settings['name'])
|
||
|
|
Gtk.main_quit()
|
||
|
|
|
||
|
|
def on_adapter_added(self, adapter_path):
|
||
|
|
def on_property_changed(name, value):
|
||
|
|
if name == 'Powered' and value:
|
||
|
|
adapter.unhandle_signal(on_property_changed, "PropertyChanged")
|
||
|
|
self.add_to_notebook(adapter)
|
||
|
|
|
||
|
|
adapter = Bluez.Adapter(adapter_path)
|
||
|
|
adapter.handle_signal(on_property_changed, "PropertyChanged")
|
||
|
|
|
||
|
|
def on_dbus_name_owner_change(self, owner):
|
||
|
|
print('org.bluez owner changed to '+owner)
|
||
|
|
if owner == '':
|
||
|
|
self.manager = None
|
||
|
|
#fixme: show error dialog and exit
|
||
|
|
|
||
|
|
def build_adapter_tab(self, adapter):
|
||
|
|
adapter_settings = {}
|
||
|
|
|
||
|
|
def on_property_changed(name, value):
|
||
|
|
if name == 'Powered':
|
||
|
|
if not value:
|
||
|
|
self.remove_from_notebook(adapter)
|
||
|
|
else:
|
||
|
|
self.add_to_notebook(adapter)
|
||
|
|
elif name == "Discoverable":
|
||
|
|
if value == 0:
|
||
|
|
hidden_radio.set_active(True)
|
||
|
|
|
||
|
|
def on_hidden_toggle(radio):
|
||
|
|
if not radio.props.active:
|
||
|
|
return
|
||
|
|
adapter_settings['discoverable'] = False
|
||
|
|
adapter.set('Discoverable', False)
|
||
|
|
hscale.set_sensitive(False)
|
||
|
|
#we need to triger "format-value" signal
|
||
|
|
hscale.set_value(1)
|
||
|
|
hscale.set_value(0)
|
||
|
|
|
||
|
|
def on_always_toggle(radio):
|
||
|
|
if not radio.props.active:
|
||
|
|
return
|
||
|
|
adapter_settings['discoverable'] = True
|
||
|
|
adapter.set('Discoverable', True)
|
||
|
|
hscale.set_sensitive(False)
|
||
|
|
#we need to triger "format-value" signal
|
||
|
|
hscale.set_value(1)
|
||
|
|
hscale.set_value(0)
|
||
|
|
|
||
|
|
def on_temporary_toggle(radio):
|
||
|
|
if not radio.props.active:
|
||
|
|
return
|
||
|
|
adapter_settings['discoverable'] = True
|
||
|
|
adapter.set('Discoverable', True)
|
||
|
|
hscale.set_sensitive(True)
|
||
|
|
hscale.set_value(3)
|
||
|
|
|
||
|
|
def on_scale_format_value(scale, value):
|
||
|
|
if value == 0:
|
||
|
|
if adapter_settings['discoverable']:
|
||
|
|
return _("Always")
|
||
|
|
else:
|
||
|
|
return _("Hidden")
|
||
|
|
else:
|
||
|
|
return gettext.ngettext("%d Minute", "%d Minutes", value) % (value)
|
||
|
|
|
||
|
|
def on_scale_value_changed(scale):
|
||
|
|
val = scale.get_value()
|
||
|
|
print('value: '+str(val))
|
||
|
|
if val == 0 and adapter_settings['discoverable']:
|
||
|
|
always_radio.props.active = True
|
||
|
|
timeout = int(val * 60)
|
||
|
|
adapter.set('DiscoverableTimeout', timeout)
|
||
|
|
|
||
|
|
def on_name_changed(entry):
|
||
|
|
adapter_settings['name'] = entry.get_text()
|
||
|
|
adapter_settings['changed'] = True
|
||
|
|
|
||
|
|
props = adapter.get_properties()
|
||
|
|
adapter_settings['adapter'] = adapter
|
||
|
|
adapter_settings['signals'] = SignalTracker()
|
||
|
|
adapter_settings['signals'].Handle(adapter, on_property_changed, "PropertyChanged")
|
||
|
|
adapter_settings['address'] = props['Address']
|
||
|
|
adapter_settings['name'] = adapter.get_name()
|
||
|
|
adapter_settings['discoverable'] = props['Discoverable']
|
||
|
|
#we use count timeout in minutes
|
||
|
|
adapter_settings['discoverable_timeout'] = props['DiscoverableTimeout'] / 60
|
||
|
|
adapter_settings['changed'] = False
|
||
|
|
|
||
|
|
builder = Gtk.Builder()
|
||
|
|
builder.set_translation_domain("blueman")
|
||
|
|
builder.add_from_file(UI_PATH + "/adapters-tab.ui")
|
||
|
|
adapter_settings['vbox'] = builder.get_object("vbox1")
|
||
|
|
|
||
|
|
hscale = builder.get_object("hscale")
|
||
|
|
hscale.set_range(0, 30)
|
||
|
|
hscale.set_increments(1, 1)
|
||
|
|
if adapter_settings['discoverable']:
|
||
|
|
hscale.set_value(adapter_settings['discoverable_timeout'])
|
||
|
|
hscale.set_sensitive(adapter_settings['discoverable_timeout'] > 0)
|
||
|
|
adapter_settings['signals'].Handle(hscale, "format-value", on_scale_format_value)
|
||
|
|
adapter_settings['signals'].Handle(hscale, "value-changed", on_scale_value_changed)
|
||
|
|
|
||
|
|
hidden_radio = builder.get_object("hidden1")
|
||
|
|
if not adapter_settings['discoverable']:
|
||
|
|
hidden_radio.set_active(True)
|
||
|
|
adapter_settings['signals'].Handle(hidden_radio, "toggled", on_hidden_toggle)
|
||
|
|
always_radio = builder.get_object("always")
|
||
|
|
if adapter_settings['discoverable'] and adapter_settings['discoverable_timeout'] == 0:
|
||
|
|
always_radio.set_active(True)
|
||
|
|
adapter_settings['signals'].Handle(always_radio, "toggled", on_always_toggle)
|
||
|
|
temporary_radio = builder.get_object("temporary")
|
||
|
|
if adapter_settings['discoverable'] and adapter_settings['discoverable_timeout'] > 0:
|
||
|
|
temporary_radio.set_active(True)
|
||
|
|
adapter_settings['signals'].Handle(temporary_radio, "toggled", on_temporary_toggle)
|
||
|
|
|
||
|
|
name_entry = builder.get_object("name_entry")
|
||
|
|
name_entry.set_text(adapter_settings['name'])
|
||
|
|
adapter_settings['signals'].Handle(name_entry, "changed", on_name_changed)
|
||
|
|
|
||
|
|
return adapter_settings
|
||
|
|
|
||
|
|
def add_to_notebook(self, adapter):
|
||
|
|
hci_dev = os.path.basename(adapter.get_object_path())
|
||
|
|
hci_dev_num = int(hci_dev[3:])
|
||
|
|
|
||
|
|
if not hci_dev in self.tabs:
|
||
|
|
self.tabs[hci_dev] = self.build_adapter_tab(adapter)
|
||
|
|
else:
|
||
|
|
if self.tabs[hci_dev]['visible']:
|
||
|
|
return
|
||
|
|
#might need to update settings at this point
|
||
|
|
settings = self.tabs[hci_dev]
|
||
|
|
settings['visible'] = True
|
||
|
|
name = settings['name']
|
||
|
|
if name == '':
|
||
|
|
name = _('Adapter') + ' %d' % (hci_dev_num + 1)
|
||
|
|
label = Gtk.Label(label=name)
|
||
|
|
label.set_max_width_chars(20)
|
||
|
|
label.props.hexpand = True
|
||
|
|
label.set_ellipsize(Pango.EllipsizeMode.END)
|
||
|
|
self.notebook.insert_page(settings['vbox'], label, hci_dev_num)
|
||
|
|
|
||
|
|
def remove_from_notebook(self, adapter):
|
||
|
|
hci_dev = os.path.basename(adapter.get_object_path())
|
||
|
|
hci_dev_num = int(hci_dev[3:])
|
||
|
|
|
||
|
|
self.tabs[hci_dev]['visible'] = False
|
||
|
|
self.notebook.remove_page(hci_dev_num)
|
||
|
|
|
||
|
|
#leave actual tab contents intact in case adapter becomes present once again
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == '__main__':
|
||
|
|
set_proc_title()
|
||
|
|
adapter_name = None
|
||
|
|
if len(sys.argv) > 1:
|
||
|
|
adapter_name = sys.argv[1]
|
||
|
|
BluemanAdapters(adapter_name)
|
||
|
|
|