├── po ├── POTFILES.skip ├── blueman.excluded.pot ├── meson.build └── LINGUAS ├── test ├── __init__.py ├── apps │ ├── __init__.py │ └── Makefile.am ├── bluez │ ├── __init__.py │ ├── obex │ │ ├── __init__.py │ │ ├── Makefile.am │ │ ├── test_manager.py │ │ └── test_imports.py │ ├── Makefile.am │ ├── test_manager.py │ └── test_imports.py ├── gui │ ├── __init__.py │ ├── applet │ │ ├── __init__.py │ │ ├── Makefile.am │ │ └── test_imports.py │ ├── manager │ │ ├── __init__.py │ │ ├── Makefile.am │ │ └── test_imports.py │ ├── Makefile.am │ └── test_imports.py ├── main │ ├── __init__.py │ ├── applet │ │ ├── __init__.py │ │ ├── Makefile.am │ │ └── test_imports.py │ ├── indicators │ │ ├── __init__.py │ │ ├── Makefile.am │ │ └── test_imports.py │ ├── Makefile.am │ ├── test_dbus_proxies.py │ ├── test_pulseaudio_utils.py │ └── test_imports.py ├── module │ ├── __init__.py │ ├── Makefile.am │ └── test_imports.py ├── plugins │ ├── __init__.py │ ├── applet │ │ ├── __init__.py │ │ ├── Makefile.am │ │ └── test_imports.py │ ├── manager │ │ ├── __init__.py │ │ ├── Makefile.am │ │ └── test_imports.py │ ├── services │ │ ├── __init__.py │ │ ├── Makefile.am │ │ └── test_imports.py │ ├── mechanism │ │ ├── __init__.py │ │ ├── Makefile.am │ │ └── test_imports.py │ ├── Makefile.am │ └── test_imports.py ├── services │ ├── __init__.py │ ├── meta │ │ ├── __init__.py │ │ ├── Makefile.am │ │ └── test_imports.py │ ├── Makefile.am │ └── test_imports.py ├── testhelpers │ ├── Makefile.am │ └── DBusMock.py ├── Makefile.am ├── test_gobject.py └── test_imports.py ├── blueman ├── __init__.py ├── gui │ ├── __init__.py │ ├── applet │ │ ├── __init__.py │ │ └── Makefile.am │ ├── manager │ │ ├── __init__.py │ │ └── Makefile.am │ ├── Makefile.am │ ├── Animation.py │ ├── GsmSettings.py │ ├── DeviceSelectorDialog.py │ └── DeviceSelectorList.py ├── bluez │ ├── __init__.py │ ├── obex │ │ ├── __init__.py │ │ ├── Base.py │ │ ├── Makefile.am │ │ ├── Session.py │ │ ├── AgentManager.py │ │ ├── ObjectPush.py │ │ ├── Transfer.py │ │ └── Client.py │ ├── Battery.py │ ├── Makefile.am │ ├── NetworkServer.py │ ├── AgentManager.py │ ├── Adapter.py │ ├── Network.py │ ├── Device.py │ └── AnyBase.py ├── config │ ├── __init__.py │ ├── Makefile.am │ └── AutoConnectConfig.py ├── main │ ├── __init__.py │ ├── applet │ │ ├── __init__.py │ │ └── Makefile.am │ ├── indicators │ │ ├── __init__.py │ │ ├── Makefile.am │ │ └── IndicatorInterface.py │ ├── Builder.py │ ├── Makefile.am │ ├── BatteryWatcher.py │ ├── SpeedCalc.py │ └── DhcpClient.py ├── plugins │ ├── __init__.py │ ├── applet │ │ ├── __init__.py │ │ ├── StatusNotifierItem.py │ │ ├── ExitItem.py │ │ ├── AuthAgent.py │ │ ├── Makefile.am │ │ ├── NMDUNSupport.py │ │ ├── NMPANSupport.py │ │ ├── DisconnectItems.py │ │ ├── ConnectionNotifier.py │ │ ├── DhcpClient.py │ │ └── GameControllerWakelock.py │ ├── manager │ │ ├── __init__.py │ │ ├── Makefile.am │ │ └── Notes.py │ ├── mechanism │ │ ├── __init__.py │ │ ├── Makefile.am │ │ ├── RfKill.py │ │ ├── Rfcomm.py │ │ ├── Ppp.py │ │ └── Network.py │ ├── services │ │ ├── __init__.py │ │ ├── Makefile.am │ │ └── Transfer.py │ ├── errors.py │ ├── ManagerPlugin.py │ ├── Makefile.am │ ├── MechanismPlugin.py │ └── ServicePlugin.py ├── services │ ├── meta │ │ ├── __init__.py │ │ ├── Makefile.am │ │ └── NetworkService.py │ ├── GroupNetwork.py │ ├── SerialPort.py │ ├── NetworkAccessPoint.py │ ├── Makefile.am │ ├── __init__.py │ ├── DialupNetwork.py │ └── Functions.py ├── gobject.py ├── Makefile.am ├── bluemantyping.py ├── Constants.py.in └── Service.py ├── stubs ├── gi │ ├── types.pyi │ ├── __init__.pyi │ └── repository │ │ ├── __init__.py │ │ ├── xlib.pyi │ │ └── GModule.pyi └── _blueman.pyi ├── data ├── icons │ ├── Makefile.am │ ├── hicolor │ │ ├── 22x22 │ │ │ ├── Makefile.am │ │ │ ├── apps │ │ │ │ ├── blueman.png │ │ │ │ └── Makefile.am │ │ │ └── status │ │ │ │ ├── blueman.png │ │ │ │ ├── blueman-active.png │ │ │ │ ├── blueman-tray.png │ │ │ │ ├── blueman-disabled.png │ │ │ │ └── Makefile.am │ │ ├── 24x24 │ │ │ ├── Makefile.am │ │ │ ├── apps │ │ │ │ ├── blueman.png │ │ │ │ └── Makefile.am │ │ │ └── status │ │ │ │ ├── blueman.png │ │ │ │ ├── blueman-active.png │ │ │ │ ├── blueman-tray.png │ │ │ │ ├── blueman-disabled.png │ │ │ │ └── Makefile.am │ │ ├── 32x32 │ │ │ ├── Makefile.am │ │ │ ├── apps │ │ │ │ ├── blueman.png │ │ │ │ └── Makefile.am │ │ │ └── status │ │ │ │ ├── blueman.png │ │ │ │ ├── blueman-active.png │ │ │ │ ├── blueman-tray.png │ │ │ │ ├── blueman-disabled.png │ │ │ │ ├── blueman-down-active.png │ │ │ │ ├── blueman-up-active.png │ │ │ │ ├── blueman-up-inactive.png │ │ │ │ ├── blueman-down-inactive.png │ │ │ │ └── Makefile.am │ │ ├── 48x48 │ │ │ ├── Makefile.am │ │ │ ├── apps │ │ │ │ ├── blueman.png │ │ │ │ └── Makefile.am │ │ │ └── status │ │ │ │ ├── blueman.png │ │ │ │ ├── blueman-active.png │ │ │ │ ├── blueman-tray.png │ │ │ │ ├── blueman-disabled.png │ │ │ │ └── Makefile.am │ │ ├── 64x64 │ │ │ ├── Makefile.am │ │ │ ├── apps │ │ │ │ ├── blueman.png │ │ │ │ └── Makefile.am │ │ │ └── status │ │ │ │ ├── blueman.png │ │ │ │ ├── blueman-active.png │ │ │ │ ├── blueman-tray.png │ │ │ │ ├── blueman-disabled.png │ │ │ │ └── Makefile.am │ │ ├── 72x72 │ │ │ ├── Makefile.am │ │ │ ├── apps │ │ │ │ ├── blueman.png │ │ │ │ └── Makefile.am │ │ │ └── status │ │ │ │ ├── blueman.png │ │ │ │ ├── blueman-active.png │ │ │ │ ├── blueman-tray.png │ │ │ │ ├── blueman-disabled.png │ │ │ │ └── Makefile.am │ │ ├── 96x96 │ │ │ ├── Makefile.am │ │ │ ├── apps │ │ │ │ ├── blueman.png │ │ │ │ └── Makefile.am │ │ │ └── status │ │ │ │ ├── blueman.png │ │ │ │ ├── blueman-active.png │ │ │ │ ├── blueman-tray.png │ │ │ │ ├── blueman-disabled.png │ │ │ │ └── Makefile.am │ │ ├── 128x128 │ │ │ ├── Makefile.am │ │ │ ├── apps │ │ │ │ ├── blueman.png │ │ │ │ └── Makefile.am │ │ │ └── status │ │ │ │ ├── blueman.png │ │ │ │ ├── blueman-tray.png │ │ │ │ ├── blueman-active.png │ │ │ │ ├── blueman-disabled.png │ │ │ │ └── Makefile.am │ │ ├── 192x192 │ │ │ ├── Makefile.am │ │ │ ├── apps │ │ │ │ ├── blueman.png │ │ │ │ └── Makefile.am │ │ │ └── status │ │ │ │ ├── blueman.png │ │ │ │ ├── blueman-tray.png │ │ │ │ ├── blueman-active.png │ │ │ │ ├── blueman-disabled.png │ │ │ │ └── Makefile.am │ │ ├── 256x256 │ │ │ ├── Makefile.am │ │ │ ├── apps │ │ │ │ ├── blueman.png │ │ │ │ └── Makefile.am │ │ │ └── status │ │ │ │ ├── blueman.png │ │ │ │ ├── blueman-tray.png │ │ │ │ ├── blueman-active.png │ │ │ │ ├── blueman-disabled.png │ │ │ │ └── Makefile.am │ │ ├── 16x16 │ │ │ ├── Makefile.am │ │ │ ├── apps │ │ │ │ ├── blueman.png │ │ │ │ └── Makefile.am │ │ │ ├── status │ │ │ │ ├── blueman.png │ │ │ │ ├── blueman-active.png │ │ │ │ ├── blueman-tray.png │ │ │ │ ├── blueman-disabled.png │ │ │ │ ├── blueman-down-active.png │ │ │ │ ├── blueman-up-active.png │ │ │ │ ├── blueman-up-inactive.png │ │ │ │ ├── blueman-down-inactive.png │ │ │ │ └── Makefile.am │ │ │ └── devices │ │ │ │ ├── blueman-device.png │ │ │ │ └── Makefile.am │ │ ├── scalable │ │ │ ├── Makefile.am │ │ │ ├── apps │ │ │ │ └── Makefile.am │ │ │ ├── devices │ │ │ │ └── Makefile.am │ │ │ ├── actions │ │ │ │ ├── blueman-trust-symbolic.svg │ │ │ │ ├── blueman-untrust-symbolic.svg │ │ │ │ ├── Makefile.am │ │ │ │ ├── blueman-block-symbolic.svg │ │ │ │ ├── blueman-send-symbolic.svg │ │ │ │ └── blueman-pair-symbolic.svg │ │ │ ├── emblems │ │ │ │ └── Makefile.am │ │ │ └── status │ │ │ │ ├── Makefile.am │ │ │ │ ├── blueman-symbolic.svg │ │ │ │ ├── blueman-tray-symbolic.svg │ │ │ │ ├── blueman-disabled-symbolic.svg │ │ │ │ └── blueman-active-symbolic.svg │ │ └── Makefile.am │ └── pixmaps │ │ ├── blueman-tpl-10.png │ │ ├── blueman-tpl-20.png │ │ ├── blueman-tpl-30.png │ │ ├── blueman-tpl-40.png │ │ ├── blueman-tpl-50.png │ │ ├── blueman-tpl-60.png │ │ ├── blueman-tpl-70.png │ │ ├── blueman-tpl-80.png │ │ ├── blueman-tpl-90.png │ │ ├── blueman-rssi-10.png │ │ ├── blueman-rssi-100.png │ │ ├── blueman-rssi-20.png │ │ ├── blueman-rssi-30.png │ │ ├── blueman-rssi-40.png │ │ ├── blueman-rssi-50.png │ │ ├── blueman-rssi-60.png │ │ ├── blueman-rssi-70.png │ │ ├── blueman-rssi-80.png │ │ ├── blueman-rssi-90.png │ │ ├── blueman-tpl-100.png │ │ ├── blueman-battery-10.png │ │ ├── blueman-battery-100.png │ │ ├── blueman-battery-20.png │ │ ├── blueman-battery-30.png │ │ ├── blueman-battery-40.png │ │ ├── blueman-battery-50.png │ │ ├── blueman-battery-60.png │ │ ├── blueman-battery-70.png │ │ ├── blueman-battery-80.png │ │ ├── blueman-battery-90.png │ │ ├── blueman-battery-100_.png │ │ └── Makefile.am ├── meson.build ├── man │ ├── blueman-tray.1 │ ├── Makefile.am │ ├── blueman-manager.1 │ ├── blueman-applet.1 │ ├── blueman-adapters.1 │ ├── blueman-services.1 │ └── blueman-sendto.1 ├── configs │ ├── org.blueman.Applet.service.in │ ├── org.blueman.Manager.service.in │ ├── blueman-manager.service.in │ ├── blueman-applet.service.in │ ├── org.blueman.Mechanism.service.in │ ├── its │ │ ├── polkit.loc │ │ └── polkit.its │ ├── blueman-mechanism.service.in │ ├── blueman.rules │ ├── org.blueman.Mechanism.conf │ ├── org.blueman.policy.in │ └── Makefile.am ├── thunar-sendto-blueman.desktop.in ├── blueman.desktop.in ├── blueman-manager.desktop.in ├── ui │ ├── Makefile.am │ ├── services-window.ui │ └── services-transfer.ui ├── blueman-adapters.desktop.in └── Makefile.am ├── .sonarcloud.properties ├── make_release.sh ├── apps ├── Makefile.am ├── blueman-rfcomm-watcher.in ├── blueman-tray.in ├── blueman-manager.in ├── blueman-applet.in ├── blueman-services.in ├── blueman-adapters.in └── blueman-mechanism.in ├── module ├── Makefile.am ├── meson.build └── libblueman.h ├── sendto ├── Makefile.am └── blueman_sendto.py.in ├── autogen.sh ├── meson_options.txt ├── Makefile.am ├── README.md └── Dependencies.md /po/POTFILES.skip: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blueman/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blueman/gui/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/apps/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/bluez/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/gui/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/main/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/module/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blueman/bluez/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blueman/config/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blueman/main/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blueman/plugins/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/bluez/obex/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/gui/applet/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/gui/manager/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/main/applet/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/plugins/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/services/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blueman/bluez/obex/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blueman/gui/applet/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blueman/gui/manager/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blueman/main/applet/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/main/indicators/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/plugins/applet/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/plugins/manager/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/plugins/services/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/services/meta/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blueman/main/indicators/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blueman/plugins/applet/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blueman/plugins/manager/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blueman/plugins/mechanism/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blueman/plugins/services/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/plugins/mechanism/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stubs/gi/types.pyi: -------------------------------------------------------------------------------- 1 | class GObjectMeta(type): 2 | pass 3 | -------------------------------------------------------------------------------- /test/apps/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = \ 2 | __init__.py 3 | -------------------------------------------------------------------------------- /data/icons/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | hicolor \ 3 | pixmaps 4 | -------------------------------------------------------------------------------- /data/meson.build: -------------------------------------------------------------------------------- 1 | gnome.compile_schemas(build_by_default: true) 2 | -------------------------------------------------------------------------------- /test/testhelpers/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = \ 2 | DBusMock.py 3 | -------------------------------------------------------------------------------- /.sonarcloud.properties: -------------------------------------------------------------------------------- 1 | sonar.sources=blueman 2 | sonar.tests=test 3 | -------------------------------------------------------------------------------- /data/icons/hicolor/22x22/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | status \ 3 | apps 4 | -------------------------------------------------------------------------------- /data/icons/hicolor/24x24/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | status \ 3 | apps 4 | -------------------------------------------------------------------------------- /data/icons/hicolor/32x32/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | status \ 3 | apps 4 | -------------------------------------------------------------------------------- /data/icons/hicolor/48x48/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | status \ 3 | apps 4 | -------------------------------------------------------------------------------- /data/icons/hicolor/64x64/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | status \ 3 | apps 4 | -------------------------------------------------------------------------------- /data/icons/hicolor/72x72/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | status \ 3 | apps 4 | -------------------------------------------------------------------------------- /data/icons/hicolor/96x96/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | status \ 3 | apps 4 | -------------------------------------------------------------------------------- /data/icons/hicolor/128x128/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | status \ 3 | apps 4 | -------------------------------------------------------------------------------- /data/icons/hicolor/192x192/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | status \ 3 | apps 4 | -------------------------------------------------------------------------------- /data/icons/hicolor/256x256/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | status \ 3 | apps 4 | -------------------------------------------------------------------------------- /data/icons/hicolor/16x16/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | devices \ 3 | status \ 4 | apps 5 | -------------------------------------------------------------------------------- /test/module/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = \ 2 | __init__.py \ 3 | test_imports.py 4 | -------------------------------------------------------------------------------- /test/services/meta/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = \ 2 | __init__.py \ 3 | test_imports.py 4 | -------------------------------------------------------------------------------- /stubs/gi/__init__.pyi: -------------------------------------------------------------------------------- 1 | def require_version(namespace: str, version: str) -> None: 2 | ... 3 | -------------------------------------------------------------------------------- /stubs/gi/repository/__init__.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | AppIndicator3: Any 4 | NM: Any 5 | -------------------------------------------------------------------------------- /test/gui/applet/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = \ 2 | __init__.py \ 3 | test_imports.py 4 | -------------------------------------------------------------------------------- /test/gui/manager/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = \ 2 | __init__.py \ 3 | test_imports.py 4 | -------------------------------------------------------------------------------- /test/main/applet/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = \ 2 | __init__.py \ 3 | test_imports.py 4 | -------------------------------------------------------------------------------- /po/blueman.excluded.pot: -------------------------------------------------------------------------------- 1 | msgid "blueman-device" 2 | msgstr "" 3 | 4 | msgid "blueman" 5 | msgstr "" 6 | -------------------------------------------------------------------------------- /test/main/indicators/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = \ 2 | __init__.py \ 3 | test_imports.py 4 | -------------------------------------------------------------------------------- /test/plugins/applet/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = \ 2 | __init__.py \ 3 | test_imports.py 4 | -------------------------------------------------------------------------------- /test/plugins/manager/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = \ 2 | __init__.py \ 3 | test_imports.py 4 | -------------------------------------------------------------------------------- /test/plugins/mechanism/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = \ 2 | __init__.py \ 3 | test_imports.py 4 | -------------------------------------------------------------------------------- /test/plugins/services/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = \ 2 | __init__.py \ 3 | test_imports.py 4 | -------------------------------------------------------------------------------- /test/bluez/obex/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = \ 2 | __init__.py \ 3 | test_imports.py \ 4 | test_manager.py 5 | -------------------------------------------------------------------------------- /data/icons/hicolor/scalable/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | devices \ 3 | emblems \ 4 | actions \ 5 | status \ 6 | apps 7 | -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-tpl-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-tpl-10.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-tpl-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-tpl-20.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-tpl-30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-tpl-30.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-tpl-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-tpl-40.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-tpl-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-tpl-50.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-tpl-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-tpl-60.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-tpl-70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-tpl-70.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-tpl-80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-tpl-80.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-tpl-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-tpl-90.png -------------------------------------------------------------------------------- /test/services/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | meta 3 | 4 | EXTRA_DIST = \ 5 | __init__.py \ 6 | test_imports.py 7 | -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-rssi-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-rssi-10.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-rssi-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-rssi-100.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-rssi-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-rssi-20.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-rssi-30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-rssi-30.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-rssi-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-rssi-40.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-rssi-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-rssi-50.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-rssi-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-rssi-60.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-rssi-70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-rssi-70.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-rssi-80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-rssi-80.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-rssi-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-rssi-90.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-tpl-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-tpl-100.png -------------------------------------------------------------------------------- /data/man/blueman-tray.1: -------------------------------------------------------------------------------- 1 | .TH BLUEMAN-TRAY 1 LOCAL 2 | 3 | .SH NAME 4 | 5 | blueman-tray - a status icon application for blueman 6 | -------------------------------------------------------------------------------- /data/icons/hicolor/16x16/apps/blueman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/16x16/apps/blueman.png -------------------------------------------------------------------------------- /data/icons/hicolor/22x22/apps/blueman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/22x22/apps/blueman.png -------------------------------------------------------------------------------- /data/icons/hicolor/24x24/apps/blueman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/24x24/apps/blueman.png -------------------------------------------------------------------------------- /data/icons/hicolor/32x32/apps/blueman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/32x32/apps/blueman.png -------------------------------------------------------------------------------- /data/icons/hicolor/48x48/apps/blueman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/48x48/apps/blueman.png -------------------------------------------------------------------------------- /data/icons/hicolor/64x64/apps/blueman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/64x64/apps/blueman.png -------------------------------------------------------------------------------- /data/icons/hicolor/72x72/apps/blueman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/72x72/apps/blueman.png -------------------------------------------------------------------------------- /data/icons/hicolor/96x96/apps/blueman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/96x96/apps/blueman.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-battery-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-battery-10.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-battery-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-battery-100.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-battery-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-battery-20.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-battery-30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-battery-30.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-battery-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-battery-40.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-battery-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-battery-50.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-battery-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-battery-60.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-battery-70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-battery-70.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-battery-80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-battery-80.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-battery-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-battery-90.png -------------------------------------------------------------------------------- /data/icons/hicolor/128x128/apps/blueman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/128x128/apps/blueman.png -------------------------------------------------------------------------------- /data/icons/hicolor/128x128/status/blueman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/128x128/status/blueman.png -------------------------------------------------------------------------------- /data/icons/hicolor/16x16/status/blueman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/16x16/status/blueman.png -------------------------------------------------------------------------------- /data/icons/hicolor/192x192/apps/blueman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/192x192/apps/blueman.png -------------------------------------------------------------------------------- /data/icons/hicolor/192x192/status/blueman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/192x192/status/blueman.png -------------------------------------------------------------------------------- /data/icons/hicolor/22x22/status/blueman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/22x22/status/blueman.png -------------------------------------------------------------------------------- /data/icons/hicolor/24x24/status/blueman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/24x24/status/blueman.png -------------------------------------------------------------------------------- /data/icons/hicolor/256x256/apps/blueman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/256x256/apps/blueman.png -------------------------------------------------------------------------------- /data/icons/hicolor/256x256/status/blueman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/256x256/status/blueman.png -------------------------------------------------------------------------------- /data/icons/hicolor/32x32/status/blueman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/32x32/status/blueman.png -------------------------------------------------------------------------------- /data/icons/hicolor/48x48/status/blueman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/48x48/status/blueman.png -------------------------------------------------------------------------------- /data/icons/hicolor/64x64/status/blueman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/64x64/status/blueman.png -------------------------------------------------------------------------------- /data/icons/hicolor/72x72/status/blueman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/72x72/status/blueman.png -------------------------------------------------------------------------------- /data/icons/hicolor/96x96/status/blueman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/96x96/status/blueman.png -------------------------------------------------------------------------------- /data/icons/pixmaps/blueman-battery-100_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/pixmaps/blueman-battery-100_.png -------------------------------------------------------------------------------- /test/gui/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | applet \ 3 | manager 4 | 5 | EXTRA_DIST = \ 6 | __init__.py \ 7 | test_imports.py 8 | -------------------------------------------------------------------------------- /blueman/plugins/errors.py: -------------------------------------------------------------------------------- 1 | class PluginException(Exception): 2 | pass 3 | 4 | 5 | class UnsupportedPlatformError(PluginException): 6 | pass 7 | -------------------------------------------------------------------------------- /data/icons/hicolor/128x128/status/blueman-tray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/128x128/status/blueman-tray.png -------------------------------------------------------------------------------- /data/icons/hicolor/16x16/status/blueman-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/16x16/status/blueman-active.png -------------------------------------------------------------------------------- /data/icons/hicolor/16x16/status/blueman-tray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/16x16/status/blueman-tray.png -------------------------------------------------------------------------------- /data/icons/hicolor/192x192/status/blueman-tray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/192x192/status/blueman-tray.png -------------------------------------------------------------------------------- /data/icons/hicolor/22x22/status/blueman-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/22x22/status/blueman-active.png -------------------------------------------------------------------------------- /data/icons/hicolor/22x22/status/blueman-tray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/22x22/status/blueman-tray.png -------------------------------------------------------------------------------- /data/icons/hicolor/24x24/status/blueman-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/24x24/status/blueman-active.png -------------------------------------------------------------------------------- /data/icons/hicolor/24x24/status/blueman-tray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/24x24/status/blueman-tray.png -------------------------------------------------------------------------------- /data/icons/hicolor/256x256/status/blueman-tray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/256x256/status/blueman-tray.png -------------------------------------------------------------------------------- /data/icons/hicolor/32x32/status/blueman-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/32x32/status/blueman-active.png -------------------------------------------------------------------------------- /data/icons/hicolor/32x32/status/blueman-tray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/32x32/status/blueman-tray.png -------------------------------------------------------------------------------- /data/icons/hicolor/48x48/status/blueman-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/48x48/status/blueman-active.png -------------------------------------------------------------------------------- /data/icons/hicolor/48x48/status/blueman-tray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/48x48/status/blueman-tray.png -------------------------------------------------------------------------------- /data/icons/hicolor/64x64/status/blueman-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/64x64/status/blueman-active.png -------------------------------------------------------------------------------- /data/icons/hicolor/64x64/status/blueman-tray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/64x64/status/blueman-tray.png -------------------------------------------------------------------------------- /data/icons/hicolor/72x72/status/blueman-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/72x72/status/blueman-active.png -------------------------------------------------------------------------------- /data/icons/hicolor/72x72/status/blueman-tray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/72x72/status/blueman-tray.png -------------------------------------------------------------------------------- /data/icons/hicolor/96x96/status/blueman-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/96x96/status/blueman-active.png -------------------------------------------------------------------------------- /data/icons/hicolor/96x96/status/blueman-tray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/96x96/status/blueman-tray.png -------------------------------------------------------------------------------- /test/bluez/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | obex 3 | 4 | EXTRA_DIST = \ 5 | __init__.py \ 6 | test_imports.py \ 7 | test_manager.py 8 | -------------------------------------------------------------------------------- /data/icons/hicolor/128x128/status/blueman-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/128x128/status/blueman-active.png -------------------------------------------------------------------------------- /data/icons/hicolor/16x16/devices/blueman-device.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/16x16/devices/blueman-device.png -------------------------------------------------------------------------------- /data/icons/hicolor/16x16/status/blueman-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/16x16/status/blueman-disabled.png -------------------------------------------------------------------------------- /data/icons/hicolor/192x192/status/blueman-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/192x192/status/blueman-active.png -------------------------------------------------------------------------------- /data/icons/hicolor/22x22/status/blueman-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/22x22/status/blueman-disabled.png -------------------------------------------------------------------------------- /data/icons/hicolor/24x24/status/blueman-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/24x24/status/blueman-disabled.png -------------------------------------------------------------------------------- /data/icons/hicolor/256x256/status/blueman-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/256x256/status/blueman-active.png -------------------------------------------------------------------------------- /data/icons/hicolor/32x32/status/blueman-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/32x32/status/blueman-disabled.png -------------------------------------------------------------------------------- /data/icons/hicolor/48x48/status/blueman-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/48x48/status/blueman-disabled.png -------------------------------------------------------------------------------- /data/icons/hicolor/64x64/status/blueman-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/64x64/status/blueman-disabled.png -------------------------------------------------------------------------------- /data/icons/hicolor/72x72/status/blueman-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/72x72/status/blueman-disabled.png -------------------------------------------------------------------------------- /data/icons/hicolor/96x96/status/blueman-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/96x96/status/blueman-disabled.png -------------------------------------------------------------------------------- /data/icons/hicolor/128x128/status/blueman-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/128x128/status/blueman-disabled.png -------------------------------------------------------------------------------- /data/icons/hicolor/16x16/status/blueman-down-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/16x16/status/blueman-down-active.png -------------------------------------------------------------------------------- /data/icons/hicolor/16x16/status/blueman-up-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/16x16/status/blueman-up-active.png -------------------------------------------------------------------------------- /data/icons/hicolor/16x16/status/blueman-up-inactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/16x16/status/blueman-up-inactive.png -------------------------------------------------------------------------------- /data/icons/hicolor/192x192/status/blueman-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/192x192/status/blueman-disabled.png -------------------------------------------------------------------------------- /data/icons/hicolor/256x256/status/blueman-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/256x256/status/blueman-disabled.png -------------------------------------------------------------------------------- /data/icons/hicolor/32x32/status/blueman-down-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/32x32/status/blueman-down-active.png -------------------------------------------------------------------------------- /data/icons/hicolor/32x32/status/blueman-up-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/32x32/status/blueman-up-active.png -------------------------------------------------------------------------------- /data/icons/hicolor/32x32/status/blueman-up-inactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/32x32/status/blueman-up-inactive.png -------------------------------------------------------------------------------- /data/configs/org.blueman.Applet.service.in: -------------------------------------------------------------------------------- 1 | [D-BUS Service] 2 | Name=org.blueman.Applet 3 | Exec=@BINDIR@/blueman-applet 4 | SystemdService=blueman-applet.service 5 | -------------------------------------------------------------------------------- /data/icons/hicolor/16x16/status/blueman-down-inactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/16x16/status/blueman-down-inactive.png -------------------------------------------------------------------------------- /data/icons/hicolor/32x32/status/blueman-down-inactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxMint/blueman/main/data/icons/hicolor/32x32/status/blueman-down-inactive.png -------------------------------------------------------------------------------- /data/thunar-sendto-blueman.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Version=1.0 4 | Name=Bluetooth Device 5 | Icon=blueman 6 | Exec=blueman-sendto %F 7 | -------------------------------------------------------------------------------- /data/configs/org.blueman.Manager.service.in: -------------------------------------------------------------------------------- 1 | [D-BUS Service] 2 | Name=org.blueman.Manager 3 | Exec=@BINDIR@/blueman-manager 4 | SystemdService=blueman-manager.service 5 | -------------------------------------------------------------------------------- /test/module/test_imports.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | 4 | class TestImports(TestCase): 5 | def test_import(self): 6 | __import__("_blueman") 7 | -------------------------------------------------------------------------------- /data/configs/blueman-manager.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Bluetooth Manager 3 | 4 | [Service] 5 | Type=dbus 6 | BusName=org.blueman.Manager 7 | ExecStart=@BINDIR@/blueman-manager 8 | -------------------------------------------------------------------------------- /data/configs/blueman-applet.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Bluetooth management applet 3 | 4 | [Service] 5 | Type=dbus 6 | BusName=org.blueman.Applet 7 | ExecStart=@BINDIR@/blueman-applet 8 | -------------------------------------------------------------------------------- /data/configs/org.blueman.Mechanism.service.in: -------------------------------------------------------------------------------- 1 | [D-BUS Service] 2 | Name=org.blueman.Mechanism 3 | Exec=@LIBEXECDIR@/blueman-mechanism 4 | User=root 5 | SystemdService=blueman-mechanism.service 6 | -------------------------------------------------------------------------------- /data/blueman.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Blueman Applet 3 | Comment=Blueman Bluetooth Manager 4 | Icon=blueman 5 | Exec=blueman-applet 6 | Terminal=false 7 | Type=Application 8 | Categories= 9 | -------------------------------------------------------------------------------- /test/plugins/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | applet \ 3 | manager \ 4 | mechanism \ 5 | services 6 | 7 | EXTRA_DIST = \ 8 | __init__.py \ 9 | test_imports.py 10 | -------------------------------------------------------------------------------- /test/main/indicators/test_imports.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | 4 | class TestImports(TestCase): 5 | def test_GtkStatusIcon_import(self): 6 | __import__("blueman.main.indicators.GtkStatusIcon") 7 | -------------------------------------------------------------------------------- /blueman/bluez/obex/Base.py: -------------------------------------------------------------------------------- 1 | from blueman.bluez.Base import Base as BlueZBase 2 | from gi.repository import Gio 3 | 4 | 5 | class Base(BlueZBase): 6 | __bus_type = Gio.BusType.SESSION 7 | __name = 'org.bluez.obex' 8 | -------------------------------------------------------------------------------- /data/man/Makefile.am: -------------------------------------------------------------------------------- 1 | man_MANS = \ 2 | blueman-adapters.1 \ 3 | blueman-applet.1 \ 4 | blueman-manager.1 \ 5 | blueman-sendto.1 \ 6 | blueman-services.1 \ 7 | blueman-tray.1 8 | 9 | EXTRA_DIST = $(man_MANS) 10 | -------------------------------------------------------------------------------- /data/configs/its/polkit.loc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/hicolor/16x16/apps/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 16x16 3 | context = apps 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman.png 9 | 10 | EXTRA_DIST = $(icons_DATA) 11 | -------------------------------------------------------------------------------- /data/icons/hicolor/22x22/apps/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 22x22 3 | context = apps 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman.png 9 | 10 | EXTRA_DIST = $(icons_DATA) 11 | -------------------------------------------------------------------------------- /data/icons/hicolor/24x24/apps/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 24x24 3 | context = apps 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman.png 9 | 10 | EXTRA_DIST = $(icons_DATA) 11 | -------------------------------------------------------------------------------- /data/icons/hicolor/32x32/apps/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 32x32 3 | context = apps 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman.png 9 | 10 | EXTRA_DIST = $(icons_DATA) 11 | -------------------------------------------------------------------------------- /data/icons/hicolor/48x48/apps/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 48x48 3 | context = apps 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman.png 9 | 10 | EXTRA_DIST = $(icons_DATA) 11 | -------------------------------------------------------------------------------- /data/icons/hicolor/64x64/apps/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 64x64 3 | context = apps 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman.png 9 | 10 | EXTRA_DIST = $(icons_DATA) 11 | -------------------------------------------------------------------------------- /data/icons/hicolor/72x72/apps/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 72x72 3 | context = apps 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman.png 9 | 10 | EXTRA_DIST = $(icons_DATA) 11 | -------------------------------------------------------------------------------- /data/icons/hicolor/96x96/apps/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 96x96 3 | context = apps 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman.png 9 | 10 | EXTRA_DIST = $(icons_DATA) 11 | -------------------------------------------------------------------------------- /blueman/services/meta/__init__.py: -------------------------------------------------------------------------------- 1 | from blueman.services.meta.NetworkService import NetworkService as NetworkService 2 | from blueman.services.meta.SerialService import SerialService as SerialService 3 | 4 | __all__ = ["NetworkService", "SerialService"] 5 | -------------------------------------------------------------------------------- /data/icons/hicolor/128x128/apps/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 128x128 3 | context = apps 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman.png 9 | 10 | EXTRA_DIST = $(icons_DATA) 11 | -------------------------------------------------------------------------------- /data/icons/hicolor/192x192/apps/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 192x192 3 | context = apps 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman.png 9 | 10 | EXTRA_DIST = $(icons_DATA) 11 | -------------------------------------------------------------------------------- /data/icons/hicolor/256x256/apps/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 256x256 3 | context = apps 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman.png 9 | 10 | EXTRA_DIST = $(icons_DATA) 11 | -------------------------------------------------------------------------------- /data/icons/hicolor/scalable/apps/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = scalable 3 | context = apps 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman.svg 9 | 10 | EXTRA_DIST = $(icons_DATA) 11 | -------------------------------------------------------------------------------- /data/icons/hicolor/16x16/devices/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 16x16 3 | context = devices 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman-device.png 9 | 10 | EXTRA_DIST = $(icons_DATA) 11 | -------------------------------------------------------------------------------- /data/blueman-manager.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Bluetooth Manager 3 | Comment=Blueman Bluetooth Manager 4 | Icon=blueman 5 | Exec=blueman-manager 6 | Terminal=false 7 | Type=Application 8 | StartupNotify=true 9 | Categories=GTK;GNOME;Settings;HardwareSettings; 10 | -------------------------------------------------------------------------------- /blueman/config/Makefile.am: -------------------------------------------------------------------------------- 1 | bluemandir = $(pythondir)/blueman/config 2 | blueman_PYTHON = \ 3 | __init__.py \ 4 | AutoConnectConfig.py 5 | 6 | CLEANFILES = \ 7 | $(BUILT_SOURCES) 8 | 9 | DISTCLEANFILES = \ 10 | $(CLEANFILES) 11 | 12 | clean-local: 13 | rm -rf *.pyc *.pyo 14 | -------------------------------------------------------------------------------- /blueman/services/GroupNetwork.py: -------------------------------------------------------------------------------- 1 | from blueman.services.meta import NetworkService 2 | from blueman.Sdp import GN_SVCLASS_ID 3 | 4 | 5 | class GroupNetwork(NetworkService): 6 | __svclass_id__ = GN_SVCLASS_ID 7 | __icon__ = "network-wireless-symbolic" 8 | __priority__ = 80 9 | -------------------------------------------------------------------------------- /blueman/services/SerialPort.py: -------------------------------------------------------------------------------- 1 | from blueman.services.meta import SerialService 2 | from blueman.Sdp import SERIAL_PORT_SVCLASS_ID 3 | 4 | 5 | class SerialPort(SerialService): 6 | __svclass_id__ = SERIAL_PORT_SVCLASS_ID 7 | __icon__ = "blueman-serial" 8 | __priority__ = 50 9 | -------------------------------------------------------------------------------- /test/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | apps \ 3 | bluez \ 4 | gui \ 5 | main \ 6 | module \ 7 | plugins \ 8 | services \ 9 | testhelpers 10 | 11 | EXTRA_DIST = \ 12 | __init__.py \ 13 | test_imports.py \ 14 | test_gobject.py 15 | -------------------------------------------------------------------------------- /test/main/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | applet \ 3 | indicators 4 | 5 | EXTRA_DIST = \ 6 | __init__.py \ 7 | test_dns_server_provider.py \ 8 | test_dbus_proxies.py \ 9 | test_imports.py \ 10 | test_netconf.py \ 11 | test_pulseaudio_utils.py 12 | -------------------------------------------------------------------------------- /data/configs/blueman-mechanism.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Bluetooth management mechanism 3 | 4 | [Service] 5 | Type=dbus 6 | KillMode=process 7 | BusName=org.blueman.Mechanism 8 | ExecStart=@LIBEXECDIR@/blueman-mechanism 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /data/icons/hicolor/scalable/devices/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = scalable 3 | context = devices 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman-device.svg \ 9 | blueman-serial.svg 10 | 11 | EXTRA_DIST = $(icons_DATA) 12 | -------------------------------------------------------------------------------- /data/man/blueman-manager.1: -------------------------------------------------------------------------------- 1 | .TH BLUEMAN-MANAGER 1 LOCAL 2 | 3 | .SH NAME 4 | 5 | blueman-manager - bluetooth device manager 6 | 7 | .SH SYNOPSIS 8 | 9 | .B blueman-manager 10 | 11 | .SH OPTIONS 12 | 13 | There are no options. 14 | 15 | .SH AUTHOR 16 | 17 | Valmantas Paliksa 18 | -------------------------------------------------------------------------------- /blueman/services/NetworkAccessPoint.py: -------------------------------------------------------------------------------- 1 | from blueman.services.meta import NetworkService 2 | from blueman.Sdp import NAP_SVCLASS_ID 3 | 4 | 5 | class NetworkAccessPoint(NetworkService): 6 | __svclass_id__ = NAP_SVCLASS_ID 7 | __icon__ = "network-wireless-symbolic" 8 | __priority__ = 81 9 | -------------------------------------------------------------------------------- /data/man/blueman-applet.1: -------------------------------------------------------------------------------- 1 | .TH BLUEMAN-APPLET 1 LOCAL 2 | 3 | .SH NAME 4 | 5 | blueman-applet - a tray applet for managing bluetooth 6 | 7 | .SH SYNOPSIS 8 | 9 | .B blueman-applet 10 | 11 | .SH OPTIONS 12 | 13 | There are no options. 14 | 15 | .SH AUTHOR 16 | 17 | Valmantas Paliksa 18 | -------------------------------------------------------------------------------- /blueman/gui/applet/Makefile.am: -------------------------------------------------------------------------------- 1 | bluemandir = $(pythondir)/blueman/gui/applet 2 | blueman_PYTHON = \ 3 | PluginDialog.py \ 4 | __init__.py 5 | 6 | CLEANFILES = \ 7 | $(BUILT_SOURCES) 8 | 9 | DISTCLEANFILES = \ 10 | $(CLEANFILES) 11 | 12 | clean-local: 13 | rm -rf *.pyc *.pyo 14 | 15 | -------------------------------------------------------------------------------- /blueman/main/applet/Makefile.am: -------------------------------------------------------------------------------- 1 | bluemandir = $(pythondir)/blueman/main/applet 2 | blueman_PYTHON = \ 3 | BluezAgent.py \ 4 | __init__.py 5 | 6 | CLEANFILES = \ 7 | $(BUILT_SOURCES) 8 | 9 | DISTCLEANFILES = \ 10 | $(CLEANFILES) 11 | 12 | clean-local: 13 | rm -rf *.pyc *.pyo 14 | 15 | -------------------------------------------------------------------------------- /data/icons/hicolor/scalable/actions/blueman-trust-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /data/icons/hicolor/scalable/actions/blueman-untrust-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /test/bluez/test_manager.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from blueman.bluez.Manager import Manager 4 | from blueman.gobject import SingletonGObjectMeta 5 | 6 | 7 | class TestManager(TestCase): 8 | def test_metaclass(self): 9 | self.assertIsInstance(Manager, SingletonGObjectMeta) 10 | -------------------------------------------------------------------------------- /test/bluez/obex/test_manager.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from blueman.bluez.obex.Manager import Manager 4 | from blueman.gobject import SingletonGObjectMeta 5 | 6 | 7 | class TestManager(TestCase): 8 | def test_metaclass(self): 9 | self.assertIsInstance(Manager, SingletonGObjectMeta) 10 | -------------------------------------------------------------------------------- /test/main/test_dbus_proxies.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from blueman.main.DBusProxies import ProxyBase 4 | from blueman.gobject import SingletonGObjectMeta 5 | 6 | 7 | class TestDBusProxies(TestCase): 8 | def test_metaclass(self): 9 | self.assertIsInstance(ProxyBase, SingletonGObjectMeta) 10 | -------------------------------------------------------------------------------- /blueman/plugins/mechanism/Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | bluemandir = $(pythondir)/blueman/plugins/mechanism 3 | 4 | blueman_PYTHON = __init__.py Network.py Ppp.py Rfcomm.py RfKill.py 5 | 6 | CLEANFILES = \ 7 | $(BUILT_SOURCES) 8 | 9 | DISTCLEANFILES = \ 10 | $(CLEANFILES) 11 | 12 | clean-local: 13 | rm -rf *.pyc *.pyo 14 | 15 | -------------------------------------------------------------------------------- /blueman/plugins/services/Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | bluemandir = $(pythondir)/blueman/plugins/services 3 | blueman_PYTHON = \ 4 | Network.py \ 5 | Transfer.py \ 6 | __init__.py 7 | 8 | CLEANFILES = \ 9 | $(BUILT_SOURCES) 10 | 11 | DISTCLEANFILES = \ 12 | $(CLEANFILES) 13 | 14 | clean-local: 15 | rm -rf *.pyc *.pyo 16 | 17 | -------------------------------------------------------------------------------- /blueman/services/meta/Makefile.am: -------------------------------------------------------------------------------- 1 | bluemandir = $(pythondir)/blueman/services/meta 2 | blueman_PYTHON = \ 3 | __init__.py \ 4 | NetworkService.py \ 5 | SerialService.py 6 | 7 | CLEANFILES = \ 8 | $(BUILT_SOURCES) 9 | 10 | DISTCLEANFILES = \ 11 | $(CLEANFILES) 12 | 13 | clean-local: 14 | rm -rf *.pyc *.pyo 15 | 16 | -------------------------------------------------------------------------------- /test/main/test_pulseaudio_utils.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from blueman.main.PulseAudioUtils import PulseAudioUtils 4 | from blueman.gobject import SingletonGObjectMeta 5 | 6 | 7 | class TestPulseaudioUtils(TestCase): 8 | def test_metaclass(self): 9 | self.assertIsInstance(PulseAudioUtils, SingletonGObjectMeta) 10 | -------------------------------------------------------------------------------- /blueman/bluez/Battery.py: -------------------------------------------------------------------------------- 1 | from blueman.bluez.AnyBase import AnyBase 2 | 3 | from blueman.bluez.Base import Base 4 | 5 | _INTERFACE = "org.bluez.Battery1" 6 | 7 | 8 | class Battery(Base): 9 | _interface_name = _INTERFACE 10 | 11 | 12 | class AnyBattery(AnyBase): 13 | def __init__(self) -> None: 14 | super().__init__(_INTERFACE) 15 | -------------------------------------------------------------------------------- /data/configs/its/polkit.its: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /blueman/main/indicators/Makefile.am: -------------------------------------------------------------------------------- 1 | bluemandir = $(pythondir)/blueman/main/indicators 2 | blueman_PYTHON = \ 3 | __init__.py \ 4 | GtkStatusIcon.py \ 5 | IndicatorInterface.py \ 6 | StatusNotifierItem.py 7 | 8 | CLEANFILES = \ 9 | $(BUILT_SOURCES) 10 | 11 | DISTCLEANFILES = \ 12 | $(CLEANFILES) 13 | 14 | clean-local: 15 | rm -rf *.pyc *.pyo 16 | 17 | -------------------------------------------------------------------------------- /data/icons/hicolor/48x48/status/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 48x48 3 | context = status 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman-active.png \ 9 | blueman-disabled.png \ 10 | blueman-tray.png \ 11 | blueman.png 12 | 13 | EXTRA_DIST = $(icons_DATA) 14 | -------------------------------------------------------------------------------- /data/icons/hicolor/scalable/emblems/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = scalable 3 | context = emblems 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman-blocked-emblem.svg \ 9 | blueman-connected-emblem.svg \ 10 | blueman-paired-emblem.svg \ 11 | blueman-trusted-emblem.svg 12 | 13 | EXTRA_DIST = $(icons_DATA) 14 | -------------------------------------------------------------------------------- /data/icons/hicolor/128x128/status/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 128x128 3 | context = status 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman-active.png \ 9 | blueman-disabled.png \ 10 | blueman-tray.png \ 11 | blueman.png 12 | 13 | EXTRA_DIST = $(icons_DATA) 14 | -------------------------------------------------------------------------------- /data/icons/hicolor/192x192/status/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 192x192 3 | context = status 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman-active.png \ 9 | blueman-disabled.png \ 10 | blueman-tray.png \ 11 | blueman.png 12 | 13 | EXTRA_DIST = $(icons_DATA) 14 | -------------------------------------------------------------------------------- /data/icons/hicolor/22x22/status/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 22x22 3 | context = status 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman-active.png \ 9 | blueman-disabled.png \ 10 | blueman-tray.png \ 11 | blueman.png 12 | 13 | EXTRA_DIST = $(icons_DATA) 14 | -------------------------------------------------------------------------------- /data/icons/hicolor/24x24/status/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 24x24 3 | context = status 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman-active.png \ 9 | blueman-disabled.png \ 10 | blueman-tray.png \ 11 | blueman.png 12 | 13 | EXTRA_DIST = $(icons_DATA) 14 | -------------------------------------------------------------------------------- /data/icons/hicolor/256x256/status/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 256x256 3 | context = status 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman-active.png \ 9 | blueman-disabled.png \ 10 | blueman-tray.png \ 11 | blueman.png 12 | 13 | EXTRA_DIST = $(icons_DATA) 14 | -------------------------------------------------------------------------------- /data/icons/hicolor/64x64/status/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 64x64 3 | context = status 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman-active.png \ 9 | blueman-disabled.png \ 10 | blueman-tray.png \ 11 | blueman.png 12 | 13 | EXTRA_DIST = $(icons_DATA) 14 | -------------------------------------------------------------------------------- /data/icons/hicolor/72x72/status/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 72x72 3 | context = status 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman-active.png \ 9 | blueman-disabled.png \ 10 | blueman-tray.png \ 11 | blueman.png 12 | 13 | EXTRA_DIST = $(icons_DATA) 14 | -------------------------------------------------------------------------------- /data/icons/hicolor/96x96/status/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 96x96 3 | context = status 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman-active.png \ 9 | blueman-disabled.png \ 10 | blueman-tray.png \ 11 | blueman.png 12 | 13 | EXTRA_DIST = $(icons_DATA) 14 | -------------------------------------------------------------------------------- /blueman/gobject.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Optional 2 | 3 | from gi.types import GObjectMeta 4 | 5 | 6 | class SingletonGObjectMeta(GObjectMeta): 7 | _instance: Optional[Any] = None 8 | 9 | def __call__(cls, *args: Any, **kwargs: Any) -> Any: 10 | if not cls._instance: 11 | cls._instance = super().__call__(*args, **kwargs) 12 | return cls._instance 13 | -------------------------------------------------------------------------------- /blueman/bluez/obex/Makefile.am: -------------------------------------------------------------------------------- 1 | bluemandir = $(pythondir)/blueman/bluez/obex 2 | blueman_PYTHON = \ 3 | AgentManager.py \ 4 | Base.py \ 5 | Client.py \ 6 | Manager.py \ 7 | ObjectPush.py \ 8 | Session.py \ 9 | Transfer.py \ 10 | __init__.py 11 | 12 | CLEANFILES = \ 13 | $(BUILT_SOURCES) 14 | 15 | DISTCLEANFILES = \ 16 | $(CLEANFILES) 17 | 18 | clean-local: 19 | rm -rf *.pyc *.pyo 20 | -------------------------------------------------------------------------------- /data/man/blueman-adapters.1: -------------------------------------------------------------------------------- 1 | .TH BLUEMAN-SENDTO 1 LOCAL 2 | 3 | .SH NAME 4 | 5 | blueman-adapters - an utility to set adapter properties 6 | 7 | .SH SYNOPSIS 8 | 9 | .B blueman-adapters [adapter] 10 | 11 | 12 | .SH OPTIONS 13 | 14 | adapter option is used to select initial tab in the dialog. Adapter should be in the form of hci0, hci1 ... hciN 15 | 16 | .SH AUTHOR 17 | 18 | Valmantas Paliksa 19 | -------------------------------------------------------------------------------- /data/ui/Makefile.am: -------------------------------------------------------------------------------- 1 | uidir = $(pkgdatadir)/ui 2 | 3 | ui_DATA = \ 4 | adapters-tab.ui \ 5 | applet-passkey.ui \ 6 | manager-main.ui \ 7 | services-network.ui \ 8 | services-transfer.ui \ 9 | send-dialog.ui \ 10 | applet-plugins-widget.ui \ 11 | gsm-settings.ui \ 12 | net-usage.ui \ 13 | note.ui \ 14 | rename-device.ui \ 15 | services-window.ui 16 | 17 | EXTRA_DIST = $(ui_DATA) 18 | -------------------------------------------------------------------------------- /data/icons/hicolor/scalable/actions/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = scalable 3 | context = actions 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman-pair-symbolic.svg \ 9 | blueman-block-symbolic.svg \ 10 | blueman-send-symbolic.svg \ 11 | blueman-trust-symbolic.svg \ 12 | blueman-untrust-symbolic.svg 13 | 14 | EXTRA_DIST = $(icons_DATA) 15 | -------------------------------------------------------------------------------- /data/blueman-adapters.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Bluetooth Adapters 3 | Comment=Set Bluetooth Adapter Properties 4 | Exec=blueman-adapters 5 | Icon=blueman-device 6 | Terminal=false 7 | Type=Application 8 | Categories=X-XFCE;Settings;HardwareSettings;X-XFCE-SettingsDialog;GTK; 9 | StartupNotify=true 10 | OnlyShowIn=XFCE;MATE; 11 | NoDisplay=false 12 | X-XfceSettingsName=Bluetooth 13 | X-XfcePluggable=true 14 | -------------------------------------------------------------------------------- /data/icons/hicolor/scalable/actions/blueman-block-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /data/man/blueman-services.1: -------------------------------------------------------------------------------- 1 | .TH BLUEMAN-SERVICES 1 LOCAL 2 | 3 | .SH NAME 4 | 5 | blueman-services - Configure local bluetooth services 6 | 7 | .SH SYNOPSIS 8 | 9 | .B blueman-services 10 | 11 | 12 | .SH DESCRIPTION 13 | 14 | blueman-servies is a graphical dialog for configuring local bluetooth services 15 | 16 | .SH OPTIONS 17 | 18 | There are no options. 19 | 20 | .SH AUTHOR 21 | 22 | Valmantas Paliksa 23 | -------------------------------------------------------------------------------- /po/meson.build: -------------------------------------------------------------------------------- 1 | gettext_args = [ 2 | '--package-name=@0@'.format(meson.project_name()), 3 | '--package-version=@0@'.format(meson.project_version()), 4 | '--copyright-holder=Copyright © 2008 - 2020 blueman project', 5 | '--msgid-bugs-address=https://github.com/blueman-project/blueman/issues' 6 | ] 7 | 8 | i18n.gettext( 9 | package_name, 10 | preset: 'glib', 11 | args: gettext_args 12 | ) 13 | -------------------------------------------------------------------------------- /blueman/plugins/ManagerPlugin.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING 2 | 3 | from blueman.plugins.BasePlugin import BasePlugin 4 | 5 | if TYPE_CHECKING: 6 | from blueman.main.Manager import Blueman 7 | 8 | 9 | class ManagerPlugin(BasePlugin): 10 | def __init__(self, parent: "Blueman"): 11 | super().__init__() 12 | self.parent = parent 13 | 14 | def on_unload(self) -> None: 15 | pass 16 | -------------------------------------------------------------------------------- /data/icons/hicolor/scalable/status/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = scalable 3 | context = status 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = blueman-active.svg blueman-disabled.svg blueman-tray.svg blueman.svg blueman-x.svg \ 8 | blueman-active-symbolic.svg blueman-disabled-symbolic.svg blueman-tray-symbolic.svg blueman-symbolic.svg 9 | 10 | EXTRA_DIST = $(icons_DATA) 11 | -------------------------------------------------------------------------------- /blueman/services/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = meta 2 | 3 | bluemandir = $(pythondir)/blueman/services 4 | blueman_PYTHON = \ 5 | __init__.py \ 6 | DialupNetwork.py \ 7 | Functions.py \ 8 | GroupNetwork.py \ 9 | NetworkAccessPoint.py \ 10 | SerialPort.py 11 | 12 | CLEANFILES = \ 13 | $(BUILT_SOURCES) 14 | 15 | DISTCLEANFILES = \ 16 | $(CLEANFILES) 17 | 18 | clean-local: 19 | rm -rf *.pyc *.pyo 20 | 21 | -------------------------------------------------------------------------------- /blueman/gui/manager/Makefile.am: -------------------------------------------------------------------------------- 1 | bluemandir = $(pythondir)/blueman/gui/manager 2 | blueman_PYTHON = \ 3 | ManagerDeviceList.py \ 4 | ManagerDeviceMenu.py \ 5 | ManagerMenu.py \ 6 | ManagerProgressbar.py \ 7 | ManagerStats.py \ 8 | ManagerToolbar.py \ 9 | __init__.py 10 | 11 | CLEANFILES = \ 12 | $(BUILT_SOURCES) 13 | 14 | DISTCLEANFILES = \ 15 | $(CLEANFILES) 16 | 17 | clean-local: 18 | rm -rf *.pyc *.pyo 19 | 20 | -------------------------------------------------------------------------------- /blueman/plugins/manager/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = 2 | 3 | 4 | bluemandir = $(pythondir)/blueman/plugins/manager 5 | 6 | blueman_PYTHON = \ 7 | Info.py \ 8 | Notes.py \ 9 | Services.py \ 10 | __init__.py 11 | 12 | if HAVE_PULSEAUDIO 13 | blueman_PYTHON += PulseAudioProfile.py 14 | endif 15 | 16 | CLEANFILES = \ 17 | $(BUILT_SOURCES) 18 | 19 | DISTCLEANFILES = \ 20 | $(CLEANFILES) 21 | 22 | clean-local: 23 | rm -rf *.pyc *.pyo 24 | 25 | -------------------------------------------------------------------------------- /data/icons/hicolor/scalable/status/blueman-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /make_release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | VERSION=$(git describe --tags --abbrev=0) 4 | NAME="blueman-$VERSION" 5 | 6 | echo "Creating tar archive" 7 | git archive --prefix="${NAME}/" --format=tar --worktree-attributes HEAD > "${NAME}.tar" 8 | 9 | echo "Compressing archive in xz format." 10 | xz --force --keep "${NAME}.tar" 11 | 12 | echo "Compressing archive in gz format." 13 | gzip --force --keep "${NAME}.tar" 14 | 15 | echo "Cleaning up." 16 | rm "${NAME}.tar" 17 | -------------------------------------------------------------------------------- /blueman/gui/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | applet \ 3 | manager 4 | 5 | bluemandir = $(pythondir)/blueman/gui 6 | blueman_PYTHON = Animation.py GsmSettings.py CommonUi.py DeviceList.py DeviceSelectorDialog.py DeviceSelectorList.py DeviceSelectorWidget.py GenericList.py GtkAnimation.py __init__.py Notification.py 7 | 8 | CLEANFILES = \ 9 | $(BUILT_SOURCES) 10 | 11 | DISTCLEANFILES = \ 12 | $(CLEANFILES) 13 | 14 | clean-local: 15 | rm -rf *.pyc *.pyo 16 | 17 | -------------------------------------------------------------------------------- /data/icons/hicolor/scalable/status/blueman-tray-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /data/icons/hicolor/32x32/status/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 32x32 3 | context = status 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman-down-active.png \ 9 | blueman-down-inactive.png \ 10 | blueman-up-active.png \ 11 | blueman-up-inactive.png \ 12 | blueman-active.png \ 13 | blueman-disabled.png \ 14 | blueman-tray.png \ 15 | blueman.png 16 | 17 | EXTRA_DIST = $(icons_DATA) 18 | -------------------------------------------------------------------------------- /data/icons/hicolor/scalable/status/blueman-disabled-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /data/configs/blueman.rules: -------------------------------------------------------------------------------- 1 | /* Allow users in wheel group to use blueman feature requiring root without authentication */ 2 | polkit.addRule(function(action, subject) { 3 | if ((action.id == "org.blueman.network.setup" || 4 | action.id == "org.blueman.dhcp.client" || 5 | action.id == "org.blueman.rfkill.setstate" || 6 | action.id == "org.blueman.pppd.pppconnect") && 7 | subject.isInGroup("wheel")) { 8 | return polkit.Result.YES; 9 | } 10 | }); 11 | -------------------------------------------------------------------------------- /blueman/plugins/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | services \ 3 | applet \ 4 | mechanism \ 5 | manager 6 | 7 | 8 | bluemandir = $(pythondir)/blueman/plugins 9 | blueman_PYTHON = \ 10 | ServicePlugin.py \ 11 | AppletPlugin.py \ 12 | MechanismPlugin.py \ 13 | ManagerPlugin.py \ 14 | BasePlugin.py \ 15 | errors.py \ 16 | __init__.py 17 | 18 | CLEANFILES = \ 19 | $(BUILT_SOURCES) 20 | 21 | DISTCLEANFILES = \ 22 | $(CLEANFILES) 23 | 24 | clean-local: 25 | rm -rf *.pyc *.pyo 26 | 27 | -------------------------------------------------------------------------------- /blueman/bluez/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = obex 2 | 3 | bluemandir = $(pythondir)/blueman/bluez 4 | blueman_PYTHON = \ 5 | __init__.py \ 6 | Adapter.py \ 7 | AgentManager.py \ 8 | AnyBase.py \ 9 | Base.py \ 10 | Battery.py \ 11 | Device.py \ 12 | errors.py \ 13 | Manager.py \ 14 | Network.py \ 15 | NetworkServer.py 16 | 17 | CLEANFILES = \ 18 | $(BUILT_SOURCES) 19 | 20 | DISTCLEANFILES = \ 21 | $(CLEANFILES) 22 | 23 | clean-local: 24 | rm -rf *.pyc *.pyo 25 | -------------------------------------------------------------------------------- /blueman/plugins/MechanismPlugin.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING 2 | 3 | if TYPE_CHECKING: 4 | from blueman.main.MechanismApplication import MechanismApplication 5 | 6 | 7 | class MechanismPlugin: 8 | def __init__(self, parent: "MechanismApplication"): 9 | self.parent = parent 10 | self.timer = self.parent.timer 11 | 12 | self.confirm_authorization = self.parent.confirm_authorization 13 | 14 | self.on_load() 15 | 16 | def on_load(self) -> None: 17 | pass 18 | -------------------------------------------------------------------------------- /data/icons/hicolor/16x16/status/Makefile.am: -------------------------------------------------------------------------------- 1 | themedir = $(icondir)/hicolor 2 | size = 16x16 3 | context = status 4 | 5 | iconsdir = $(themedir)/$(size)/$(context) 6 | 7 | icons_DATA = \ 8 | blueman-down-active.png \ 9 | blueman-down-inactive.png \ 10 | blueman-up-active.png \ 11 | blueman-up-inactive.png \ 12 | blueman-active.png \ 13 | blueman-disabled.png \ 14 | blueman-tray.png \ 15 | blueman.png 16 | 17 | EXTRA_DIST = $(icons_DATA) 18 | -------------------------------------------------------------------------------- /po/LINGUAS: -------------------------------------------------------------------------------- 1 | af 2 | am 3 | ar 4 | ast 5 | be 6 | bg 7 | bs 8 | ca 9 | cs 10 | da 11 | de 12 | el 13 | en_AU 14 | en_GB 15 | eo 16 | es 17 | et 18 | eu 19 | fa 20 | fi 21 | fr 22 | ga 23 | gl 24 | he 25 | hi 26 | hr 27 | hu 28 | id 29 | ie 30 | is 31 | it 32 | ja 33 | ka 34 | kk 35 | ko 36 | lt 37 | lv 38 | mk 39 | mr 40 | ms 41 | nb 42 | nds 43 | nl 44 | pl 45 | pt_BR 46 | pt 47 | ro 48 | ru 49 | sk 50 | sl 51 | sq 52 | sr 53 | sv 54 | sw 55 | ta 56 | tr 57 | uk 58 | vi 59 | zh_CN 60 | zh_HK 61 | zh_TW 62 | cy 63 | oc 64 | -------------------------------------------------------------------------------- /data/man/blueman-sendto.1: -------------------------------------------------------------------------------- 1 | .TH BLUEMAN-SENDTO 1 LOCAL 2 | 3 | .SH NAME 4 | 5 | blueman-sendto - application for sending files to bluetooth devices 6 | 7 | .SH SYNOPSIS 8 | 9 | .B blueman-sendto [options] 10 | 11 | .SH DESCRIPTION 12 | 13 | blueman-sendto sends files to specified device, if there is no device specified, it will display a dialog to choose from all known devices. 14 | 15 | .SH OPTIONS 16 | .IP --device=ADDRESS 17 | Address of the device to send to 18 | 19 | 20 | .SH AUTHOR 21 | 22 | Valmantas Paliksa 23 | -------------------------------------------------------------------------------- /blueman/services/__init__.py: -------------------------------------------------------------------------------- 1 | from blueman.services.DialupNetwork import DialupNetwork as DialupNetwork 2 | from blueman.services.GroupNetwork import GroupNetwork as GroupNetwork 3 | from blueman.services.NetworkAccessPoint import NetworkAccessPoint as NetworkAccessPoint 4 | from blueman.services.SerialPort import SerialPort as SerialPort 5 | from blueman.services.Functions import get_service as get_service, get_services as get_services 6 | 7 | __all__ = ["DialupNetwork", "GroupNetwork", "NetworkAccessPoint", "SerialPort", "get_service", "get_services"] 8 | -------------------------------------------------------------------------------- /apps/Makefile.am: -------------------------------------------------------------------------------- 1 | APPS = \ 2 | blueman-adapters \ 3 | blueman-applet \ 4 | blueman-manager \ 5 | blueman-services \ 6 | blueman-sendto \ 7 | blueman-tray 8 | 9 | appsdir = $(bindir) 10 | apps_SCRIPTS = $(APPS) 11 | 12 | polkitdir = $(libexecdir) 13 | polkit_SCRIPTS = \ 14 | blueman-mechanism 15 | 16 | helpersdir = $(libexecdir) 17 | helpers_SCRIPTS = blueman-rfcomm-watcher 18 | 19 | ALL_APPS = $(APPS) $(polkit_SCRIPTS) $(helpers_SCRIPTS) 20 | 21 | EXTRA_DIST = $(ALL_APPS) $(addsuffix .in, $(ALL_APPS)) 22 | 23 | CLEANFILES = $(ALL_APPS) 24 | -------------------------------------------------------------------------------- /blueman/bluez/obex/Session.py: -------------------------------------------------------------------------------- 1 | from blueman.bluemantyping import ObjectPath, BtAddress 2 | from blueman.bluez.obex.Base import Base 3 | 4 | 5 | class Session(Base): 6 | _interface_name = 'org.bluez.obex.Session1' 7 | 8 | def __init__(self, obj_path: ObjectPath): 9 | super().__init__(obj_path=obj_path) 10 | 11 | @property 12 | def address(self) -> BtAddress: 13 | dest: BtAddress = self.get('Destination') 14 | return dest 15 | 16 | @property 17 | def root(self) -> str: 18 | root: str = self.get('Root') 19 | return root 20 | -------------------------------------------------------------------------------- /blueman/plugins/applet/StatusNotifierItem.py: -------------------------------------------------------------------------------- 1 | from gettext import gettext as _ 2 | 3 | from typing import Tuple 4 | 5 | from blueman.plugins.AppletPlugin import AppletPlugin 6 | from blueman.plugins.applet.StatusIcon import StatusIconImplementationProvider 7 | 8 | 9 | class StatusNotifierItem(AppletPlugin, StatusIconImplementationProvider): 10 | __description__ = _("Provides a StatusNotifierItem to show a statusicon") 11 | __icon__ = "bluetooth-symbolic" 12 | 13 | def on_query_status_icon_implementation(self) -> Tuple[str, int]: 14 | return "StatusNotifierItem", 20 15 | -------------------------------------------------------------------------------- /module/Makefile.am: -------------------------------------------------------------------------------- 1 | BUILT_SOURCES = _blueman.c 2 | 3 | bluemanlibdir = $(pyexecdir) 4 | bluemanlib_LTLIBRARIES = _blueman.la 5 | _blueman_la_CFLAGS = $(BLUEZ_CFLAGS) $(PYTHON_CFLAGS) \ 6 | -DSN_API_NOT_YET_FROZEN 7 | _blueman_la_LDFLAGS = -module -avoid-version -fPIC 8 | _blueman_la_LIBADD = $(BLUEZ_LIBS) $(PYTHON_LIBS) 9 | nodist__blueman_la_SOURCES = _blueman.c 10 | _blueman_la_SOURCES = \ 11 | libblueman.c \ 12 | libblueman.h 13 | 14 | _blueman.c: _blueman.pyx 15 | $(CYTHONEXEC) -o $@ $< 16 | 17 | EXTRA_DIST = _blueman.pyx meson.build 18 | CLEANFILES = _blueman.c 19 | -------------------------------------------------------------------------------- /blueman/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | bluez \ 3 | config \ 4 | gui \ 5 | main \ 6 | plugins \ 7 | services 8 | 9 | bluemandir = $(pythondir)/blueman 10 | blueman_PYTHON = \ 11 | Constants.py \ 12 | DeviceClass.py \ 13 | Functions.py \ 14 | Sdp.py \ 15 | Service.py \ 16 | gobject.py \ 17 | bluemantyping.py \ 18 | __init__.py 19 | 20 | CLEANFILES = \ 21 | Constants.py \ 22 | $(BUILT_SOURCES) 23 | 24 | DISTCLEANFILES = \ 25 | $(CLEANFILES) 26 | 27 | EXTRA_DIST = \ 28 | Constants.py.in 29 | 30 | clean-local: 31 | find . \( -name \*.pyc -o -name \*.pyo -o -name __pycache__ \) -prune -exec rm -rf {} + 32 | 33 | -------------------------------------------------------------------------------- /blueman/main/Builder.py: -------------------------------------------------------------------------------- 1 | from typing import TypeVar, Type 2 | 3 | import gi 4 | from blueman.Constants import UI_PATH 5 | 6 | gi.require_version("Gtk", "3.0") 7 | from gi.repository import Gtk 8 | 9 | 10 | class Builder(Gtk.Builder): 11 | def __init__(self, filename: str): 12 | super().__init__(translation_domain="blueman") 13 | self.add_from_file(UI_PATH + "/" + filename) 14 | 15 | T = TypeVar("T", bound=Gtk.Widget) 16 | 17 | def get_widget(self, name: str, widget_type: Type[T]) -> T: 18 | widget = self.get_object(name) 19 | assert isinstance(widget, widget_type) 20 | return widget 21 | -------------------------------------------------------------------------------- /test/test_gobject.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from gi.repository.GObject import GObject 4 | 5 | from blueman.gobject import SingletonGObjectMeta 6 | 7 | 8 | class TestGObjectMeta(TestCase): 9 | class A(GObject, metaclass=SingletonGObjectMeta): 10 | pass 11 | 12 | class B(GObject, metaclass=SingletonGObjectMeta): 13 | pass 14 | 15 | def test_instantiation(self): 16 | self.assertIsInstance(self.A(), GObject) 17 | 18 | def test_singleton(self): 19 | self.assertEqual(self.A(), self.A()) 20 | 21 | def test_separation(self): 22 | self.assertNotEqual(self.A(), self.B()) 23 | -------------------------------------------------------------------------------- /stubs/gi/repository/xlib.pyi: -------------------------------------------------------------------------------- 1 | import builtins 2 | import typing 3 | 4 | from gi.repository import GObject 5 | 6 | 7 | class Display(): 8 | ... 9 | 10 | 11 | class Screen(): 12 | ... 13 | 14 | 15 | class Visual(): 16 | ... 17 | 18 | 19 | class XConfigureEvent(): 20 | ... 21 | 22 | 23 | class XFontStruct(): 24 | ... 25 | 26 | 27 | class XImage(): 28 | ... 29 | 30 | 31 | class XTrapezoid(): 32 | ... 33 | 34 | 35 | class XVisualInfo(): 36 | ... 37 | 38 | 39 | class XWindowAttributes(): 40 | ... 41 | 42 | 43 | class XEvent(): 44 | ... 45 | 46 | 47 | def open_display() -> None: ... 48 | 49 | 50 | -------------------------------------------------------------------------------- /data/configs/org.blueman.Mechanism.conf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /blueman/plugins/applet/ExitItem.py: -------------------------------------------------------------------------------- 1 | from gettext import gettext as _ 2 | from blueman.plugins.AppletPlugin import AppletPlugin 3 | 4 | 5 | class ExitItem(AppletPlugin): 6 | __depends__ = ["Menu"] 7 | __description__ = _("Adds an exit menu item to quit the applet") 8 | __author__ = "Walmis" 9 | __icon__ = "application-exit-symbolic" 10 | 11 | def on_load(self) -> None: 12 | self.parent.Plugins.Menu.add(self, 100, text=_("_Exit"), icon_name='application-exit-symbolic', 13 | callback=self.parent.quit) 14 | 15 | def on_unload(self) -> None: 16 | self.parent.Plugins.Menu.unregister(self) 17 | -------------------------------------------------------------------------------- /blueman/bluez/NetworkServer.py: -------------------------------------------------------------------------------- 1 | from blueman.bluemantyping import ObjectPath 2 | 3 | from blueman.bluez.Base import Base 4 | from gi.repository import GLib 5 | 6 | 7 | class NetworkServer(Base): 8 | _interface_name = 'org.bluez.NetworkServer1' 9 | 10 | def __init__(self, obj_path: ObjectPath): 11 | super().__init__(obj_path=obj_path) 12 | 13 | def register(self, uuid: str, bridge: str) -> None: 14 | param = GLib.Variant('(ss)', (uuid, bridge)) 15 | self._call('Register', param) 16 | 17 | def unregister(self, uuid: str) -> None: 18 | param = GLib.Variant('(s)', (uuid,)) 19 | self._call('Unregister', param) 20 | -------------------------------------------------------------------------------- /data/icons/hicolor/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | scalable \ 3 | 16x16 \ 4 | 22x22 \ 5 | 24x24 \ 6 | 32x32 \ 7 | 48x48 \ 8 | 64x64 \ 9 | 72x72 \ 10 | 96x96 \ 11 | 128x128 \ 12 | 192x192 \ 13 | 256x256 14 | 15 | gtk_update_icon_cache = gtk-update-icon-cache -f -t $(icondir)/hicolor 16 | 17 | install-data-hook: update-icon-cache 18 | uninstall-hook: update-icon-cache 19 | update-icon-cache: 20 | @-if test -z "$(DESTDIR)"; then \ 21 | echo "Updating GTK icon cache"; \ 22 | $(gtk_update_icon_cache); \ 23 | else \ 24 | echo "*** Icon cache not updated. After (un)install, run this:"; \ 25 | echo "*** $(gtk_update_icon_cache)"; \ 26 | fi 27 | 28 | -------------------------------------------------------------------------------- /apps/blueman-rfcomm-watcher.in: -------------------------------------------------------------------------------- 1 | #!@PYTHON@ 2 | 3 | from gi.repository import Gio 4 | from gi.repository import GLib 5 | import os 6 | import argparse 7 | 8 | from blueman.Functions import set_proc_title, open_rfcomm 9 | 10 | 11 | def on_file_changed(monitor, file, other_file, event_type): 12 | if event_type == Gio.FileMonitorEvent.DELETED: 13 | loop.quit() 14 | 15 | parser = argparse.ArgumentParser() 16 | parser.add_argument("path", action="store") 17 | args = parser.parse_args() 18 | 19 | mon = Gio.File.new_for_path(args.path).monitor_file(Gio.FileMonitorFlags.NONE) 20 | mon.connect('changed', on_file_changed) 21 | 22 | fd = open_rfcomm(args.path, os.O_RDONLY) 23 | 24 | set_proc_title() 25 | loop = GLib.MainLoop() 26 | loop.run() 27 | -------------------------------------------------------------------------------- /blueman/bluemantyping.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Tuple, Union, TYPE_CHECKING, NewType 2 | from gi.repository import GObject 3 | 4 | if TYPE_CHECKING: 5 | from typing_extensions import Protocol 6 | 7 | class _HasGType(Protocol): 8 | __gtype__: GObject.GType 9 | 10 | # Actually supported types are int, bool, str, float, and object but no subclasses, see 11 | # https://github.com/GNOME/pygobject/blob/ac576400ecd554879c906791e6638d64bb8bcc2a/gi/pygi-type.c#L498 12 | # (We shield the possibility to provide a str to avoid errors) 13 | GSignals = Dict[str, Tuple[GObject.SignalFlags, None, Tuple[Union[None, type, GObject.GType, "_HasGType"], ...]]] 14 | 15 | ObjectPath = NewType("ObjectPath", str) 16 | BtAddress = NewType("BtAddress", str) 17 | -------------------------------------------------------------------------------- /blueman/main/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = applet indicators 2 | 3 | bluemandir = $(pythondir)/blueman/main 4 | 5 | blueman_PYTHON = \ 6 | PPPConnection.py \ 7 | DhcpClient.py \ 8 | __init__.py \ 9 | NetConf.py \ 10 | SpeedCalc.py \ 11 | DbusService.py \ 12 | DNSServerProvider.py \ 13 | PluginManager.py \ 14 | Adapter.py \ 15 | Applet.py \ 16 | Builder.py \ 17 | Manager.py \ 18 | MechanismApplication.py \ 19 | Sendto.py \ 20 | Services.py \ 21 | Tray.py \ 22 | DBusProxies.py \ 23 | NetworkManager.py \ 24 | BatteryWatcher.py 25 | 26 | if HAVE_PULSEAUDIO 27 | blueman_PYTHON += PulseAudioUtils.py 28 | endif 29 | 30 | 31 | CLEANFILES = \ 32 | $(BUILT_SOURCES) 33 | 34 | DISTCLEANFILES = \ 35 | $(CLEANFILES) 36 | 37 | clean-local: 38 | rm -rf *.pyc *.pyo 39 | 40 | -------------------------------------------------------------------------------- /data/icons/hicolor/scalable/actions/blueman-send-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/bluez/test_imports.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import pkgutil 3 | from unittest import TestCase, TestSuite 4 | 5 | 6 | class TestImports(TestCase): 7 | def __init__(self, mod_name): 8 | name = f"test_{mod_name.replace('.', '_')}_import" 9 | 10 | def f(): 11 | __import__(mod_name) 12 | 13 | setattr(self, name, f) 14 | super().__init__(name) 15 | 16 | 17 | def load_tests(*_args): 18 | test_cases = TestSuite() 19 | home, subpath = os.path.dirname(__file__).rsplit("/test/", 1) 20 | for package in pkgutil.iter_modules([f"{home}/blueman/{subpath}"], f"blueman.{subpath.replace('/', '.')}."): 21 | test_cases.addTest(TestImports(package.name)) 22 | 23 | assert test_cases.countTestCases() > 0 24 | 25 | return test_cases 26 | -------------------------------------------------------------------------------- /test/gui/test_imports.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import pkgutil 3 | from unittest import TestCase, TestSuite 4 | 5 | 6 | class TestImports(TestCase): 7 | def __init__(self, mod_name): 8 | name = f"test_{mod_name.replace('.', '_')}_import" 9 | 10 | def f(): 11 | __import__(mod_name) 12 | 13 | setattr(self, name, f) 14 | super().__init__(name) 15 | 16 | 17 | def load_tests(*_args): 18 | test_cases = TestSuite() 19 | home, subpath = os.path.dirname(__file__).rsplit("/test/", 1) 20 | for package in pkgutil.iter_modules([f"{home}/blueman/{subpath}"], f"blueman.{subpath.replace('/', '.')}."): 21 | test_cases.addTest(TestImports(package.name)) 22 | 23 | assert test_cases.countTestCases() > 0 24 | 25 | return test_cases 26 | -------------------------------------------------------------------------------- /test/plugins/test_imports.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import pkgutil 3 | from unittest import TestCase, TestSuite 4 | 5 | 6 | class TestImports(TestCase): 7 | def __init__(self, mod_name): 8 | name = f"test_{mod_name.replace('.', '_')}_import" 9 | 10 | def f(): 11 | __import__(mod_name) 12 | 13 | setattr(self, name, f) 14 | super().__init__(name) 15 | 16 | 17 | def load_tests(*_args): 18 | test_cases = TestSuite() 19 | home, subpath = os.path.dirname(__file__).rsplit("/test/", 1) 20 | for package in pkgutil.iter_modules([f"{home}/blueman/{subpath}"], f"blueman.{subpath.replace('/', '.')}."): 21 | test_cases.addTest(TestImports(package.name)) 22 | 23 | assert test_cases.countTestCases() > 0 24 | 25 | return test_cases 26 | -------------------------------------------------------------------------------- /test/bluez/obex/test_imports.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import pkgutil 3 | from unittest import TestCase, TestSuite 4 | 5 | 6 | class TestImports(TestCase): 7 | def __init__(self, mod_name): 8 | name = f"test_{mod_name.replace('.', '_')}_import" 9 | 10 | def f(): 11 | __import__(mod_name) 12 | 13 | setattr(self, name, f) 14 | super().__init__(name) 15 | 16 | 17 | def load_tests(*_args): 18 | test_cases = TestSuite() 19 | home, subpath = os.path.dirname(__file__).rsplit("/test/", 1) 20 | for package in pkgutil.iter_modules([f"{home}/blueman/{subpath}"], f"blueman.{subpath.replace('/', '.')}."): 21 | test_cases.addTest(TestImports(package.name)) 22 | 23 | assert test_cases.countTestCases() > 0 24 | 25 | return test_cases 26 | -------------------------------------------------------------------------------- /test/gui/applet/test_imports.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import pkgutil 3 | from unittest import TestCase, TestSuite 4 | 5 | 6 | class TestImports(TestCase): 7 | def __init__(self, mod_name): 8 | name = f"test_{mod_name.replace('.', '_')}_import" 9 | 10 | def f(): 11 | __import__(mod_name) 12 | 13 | setattr(self, name, f) 14 | super().__init__(name) 15 | 16 | 17 | def load_tests(*_args): 18 | test_cases = TestSuite() 19 | home, subpath = os.path.dirname(__file__).rsplit("/test/", 1) 20 | for package in pkgutil.iter_modules([f"{home}/blueman/{subpath}"], f"blueman.{subpath.replace('/', '.')}."): 21 | test_cases.addTest(TestImports(package.name)) 22 | 23 | assert test_cases.countTestCases() > 0 24 | 25 | return test_cases 26 | -------------------------------------------------------------------------------- /test/gui/manager/test_imports.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import pkgutil 3 | from unittest import TestCase, TestSuite 4 | 5 | 6 | class TestImports(TestCase): 7 | def __init__(self, mod_name): 8 | name = f"test_{mod_name.replace('.', '_')}_import" 9 | 10 | def f(): 11 | __import__(mod_name) 12 | 13 | setattr(self, name, f) 14 | super().__init__(name) 15 | 16 | 17 | def load_tests(*_args): 18 | test_cases = TestSuite() 19 | home, subpath = os.path.dirname(__file__).rsplit("/test/", 1) 20 | for package in pkgutil.iter_modules([f"{home}/blueman/{subpath}"], f"blueman.{subpath.replace('/', '.')}."): 21 | test_cases.addTest(TestImports(package.name)) 22 | 23 | assert test_cases.countTestCases() > 0 24 | 25 | return test_cases 26 | -------------------------------------------------------------------------------- /test/main/applet/test_imports.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import pkgutil 3 | from unittest import TestCase, TestSuite 4 | 5 | 6 | class TestImports(TestCase): 7 | def __init__(self, mod_name): 8 | name = f"test_{mod_name.replace('.', '_')}_import" 9 | 10 | def f(): 11 | __import__(mod_name) 12 | 13 | setattr(self, name, f) 14 | super().__init__(name) 15 | 16 | 17 | def load_tests(*_args): 18 | test_cases = TestSuite() 19 | home, subpath = os.path.dirname(__file__).rsplit("/test/", 1) 20 | for package in pkgutil.iter_modules([f"{home}/blueman/{subpath}"], f"blueman.{subpath.replace('/', '.')}."): 21 | test_cases.addTest(TestImports(package.name)) 22 | 23 | assert test_cases.countTestCases() > 0 24 | 25 | return test_cases 26 | -------------------------------------------------------------------------------- /test/services/test_imports.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import pkgutil 3 | from unittest import TestCase, TestSuite 4 | 5 | 6 | class TestImports(TestCase): 7 | def __init__(self, mod_name): 8 | name = f"test_{mod_name.replace('.', '_')}_import" 9 | 10 | def f(): 11 | __import__(mod_name) 12 | 13 | setattr(self, name, f) 14 | super().__init__(name) 15 | 16 | 17 | def load_tests(*_args): 18 | test_cases = TestSuite() 19 | home, subpath = os.path.dirname(__file__).rsplit("/test/", 1) 20 | for package in pkgutil.iter_modules([f"{home}/blueman/{subpath}"], f"blueman.{subpath.replace('/', '.')}."): 21 | test_cases.addTest(TestImports(package.name)) 22 | 23 | assert test_cases.countTestCases() > 0 24 | 25 | return test_cases 26 | -------------------------------------------------------------------------------- /test/plugins/services/test_imports.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import pkgutil 3 | from unittest import TestCase, TestSuite 4 | 5 | 6 | class TestImports(TestCase): 7 | def __init__(self, mod_name): 8 | name = f"test_{mod_name.replace('.', '_')}_import" 9 | 10 | def f(): 11 | __import__(mod_name) 12 | 13 | setattr(self, name, f) 14 | super().__init__(name) 15 | 16 | 17 | def load_tests(*_args): 18 | test_cases = TestSuite() 19 | home, subpath = os.path.dirname(__file__).rsplit("/test/", 1) 20 | for package in pkgutil.iter_modules([f"{home}/blueman/{subpath}"], f"blueman.{subpath.replace('/', '.')}."): 21 | test_cases.addTest(TestImports(package.name)) 22 | 23 | assert test_cases.countTestCases() > 0 24 | 25 | return test_cases 26 | -------------------------------------------------------------------------------- /test/services/meta/test_imports.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import pkgutil 3 | from unittest import TestCase, TestSuite 4 | 5 | 6 | class TestImports(TestCase): 7 | def __init__(self, mod_name): 8 | name = f"test_{mod_name.replace('.', '_')}_import" 9 | 10 | def f(): 11 | __import__(mod_name) 12 | 13 | setattr(self, name, f) 14 | super().__init__(name) 15 | 16 | 17 | def load_tests(*_args): 18 | test_cases = TestSuite() 19 | home, subpath = os.path.dirname(__file__).rsplit("/test/", 1) 20 | for package in pkgutil.iter_modules([f"{home}/blueman/{subpath}"], f"blueman.{subpath.replace('/', '.')}."): 21 | test_cases.addTest(TestImports(package.name)) 22 | 23 | assert test_cases.countTestCases() > 0 24 | 25 | return test_cases 26 | -------------------------------------------------------------------------------- /module/meson.build: -------------------------------------------------------------------------------- 1 | cython = find_program('cython', 'cython3' ,required: true) 2 | 3 | cython_outfile = join_paths(meson.project_build_root(), '@OUTPUT@') 4 | cython_infile = join_paths(meson.project_build_root(), '@INPUT@') 5 | blueman_c = custom_target( 6 | 'blueman_c', 7 | output: '_blueman.c', 8 | input: '_blueman.pyx', 9 | command: [cython, '-w', meson.project_source_root(), '--output-file', cython_outfile, cython_infile]) 10 | 11 | sources = [ 12 | blueman_c, 13 | 'libblueman.c', 14 | 'libblueman.h' 15 | ] 16 | 17 | deps = [pygobject, pythonlib, gthread, bluez] 18 | 19 | bluemanlib = shared_library( 20 | '_blueman', sources, 21 | name_prefix: '', 22 | dependencies : deps, 23 | c_args: ['-DSN_API_NOT_YET_FROZEN'], 24 | install: true, 25 | install_dir: pythondir, 26 | ) 27 | -------------------------------------------------------------------------------- /blueman/plugins/mechanism/RfKill.py: -------------------------------------------------------------------------------- 1 | import os 2 | import struct 3 | from blueman.plugins.MechanismPlugin import MechanismPlugin 4 | from blueman.plugins.applet.KillSwitch import RFKILL_TYPE_BLUETOOTH, RFKILL_OP_CHANGE_ALL 5 | 6 | if not os.path.exists('/dev/rfkill'): 7 | raise ImportError("Hardware kill switch not found") 8 | 9 | 10 | class RfKill(MechanismPlugin): 11 | def on_load(self) -> None: 12 | self.parent.add_method("SetRfkillState", ("b",), "", self._set_rfkill_state, pass_sender=True) 13 | 14 | def _set_rfkill_state(self, state: bool, caller: str) -> None: 15 | self.confirm_authorization(caller, "org.blueman.rfkill.setstate") 16 | with open('/dev/rfkill', 'r+b', buffering=0) as f: 17 | f.write(struct.pack("IBBBB", 0, RFKILL_TYPE_BLUETOOTH, RFKILL_OP_CHANGE_ALL, (0 if state else 1), 0)) 18 | -------------------------------------------------------------------------------- /blueman/main/indicators/IndicatorInterface.py: -------------------------------------------------------------------------------- 1 | from abc import abstractmethod, ABCMeta 2 | from typing import Iterable, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from blueman.plugins.applet.Menu import MenuItemDict 6 | 7 | 8 | class IndicatorNotAvailable(RuntimeError): 9 | pass 10 | 11 | 12 | class IndicatorInterface(metaclass=ABCMeta): 13 | @abstractmethod 14 | def set_icon(self, icon_name: str) -> None: 15 | ... 16 | 17 | @abstractmethod 18 | def set_tooltip_title(self, title: str) -> None: 19 | ... 20 | 21 | @abstractmethod 22 | def set_tooltip_text(self, text: str) -> None: 23 | ... 24 | 25 | @abstractmethod 26 | def set_visibility(self, visible: bool) -> None: 27 | ... 28 | 29 | @abstractmethod 30 | def set_menu(self, menu: Iterable["MenuItemDict"]) -> None: 31 | ... 32 | -------------------------------------------------------------------------------- /blueman/services/DialupNetwork.py: -------------------------------------------------------------------------------- 1 | from gettext import gettext as _ 2 | from typing import Set 3 | 4 | from blueman.Service import Action 5 | from blueman.services.meta import SerialService 6 | from blueman.Sdp import DIALUP_NET_SVCLASS_ID 7 | from blueman.gui.GsmSettings import GsmSettings 8 | 9 | 10 | class DialupNetwork(SerialService): 11 | __svclass_id__ = DIALUP_NET_SVCLASS_ID 12 | __icon__ = "modem" 13 | __priority__ = 50 14 | 15 | @property 16 | def common_actions(self) -> Set[Action]: 17 | def open_settings() -> None: 18 | d = GsmSettings(self.device['Address']) 19 | d.run() 20 | d.destroy() 21 | 22 | return {Action( 23 | _("Dialup Settings"), 24 | "preferences-other", 25 | {'PPPSupport', 'NMDUNSupport'}, 26 | open_settings 27 | )} 28 | -------------------------------------------------------------------------------- /blueman/plugins/applet/AuthAgent.py: -------------------------------------------------------------------------------- 1 | from gettext import gettext as _ 2 | 3 | from blueman.plugins.AppletPlugin import AppletPlugin 4 | from blueman.main.applet.BluezAgent import BluezAgent 5 | 6 | 7 | class AuthAgent(AppletPlugin): 8 | __description__ = _("Provides passkey, authentication services for BlueZ daemon") 9 | __icon__ = "blueman-pair-symbolic" 10 | __author__ = "Walmis" 11 | 12 | _agent = None 13 | 14 | def on_unload(self) -> None: 15 | if self._agent: 16 | self._agent.unregister_agent() 17 | self._agent = None 18 | 19 | def on_manager_state_changed(self, state: bool) -> None: 20 | if state: 21 | self._agent = BluezAgent() 22 | self._agent.register_agent() 23 | else: 24 | # At this point bluez already called Release on the agent 25 | self._agent = None 26 | -------------------------------------------------------------------------------- /sendto/Makefile.am: -------------------------------------------------------------------------------- 1 | caja_blueman_sendto.py: blueman_sendto.py.in 2 | $(SED) -e "s|@FILEMANAGER@|Caja|" $< >$@ 3 | 4 | nemo_blueman_sendto.py: blueman_sendto.py.in 5 | $(SED) -e "s|@FILEMANAGER@|Nemo|" $< >$@ 6 | 7 | nautilus_blueman_sendto.py: blueman_sendto.py.in 8 | $(SED) -e "s|@FILEMANAGER@|Nautilus|" $< >$@ 9 | 10 | if HAVE_CAJA_PYTHON 11 | cajasendtodir = $(datadir)/caja-python/extensions 12 | cajasendto_DATA = caja_blueman_sendto.py 13 | endif 14 | 15 | if HAVE_NEMO_PYTHON 16 | nemosendtodir = $(datadir)/nemo-python/extensions 17 | nemosendto_DATA = nemo_blueman_sendto.py 18 | endif 19 | 20 | if HAVE_NAUTILUS_PYTHON 21 | nautilussendtodir = $(datadir)/nautilus-python/extensions 22 | nautilussendto_DATA = nautilus_blueman_sendto.py 23 | endif 24 | 25 | CLEANFILES = caja_blueman_sendto.py nemo_blueman_sendto.py nautilus_blueman_sendto.py 26 | EXTRA_DIST = blueman_sendto.py.in 27 | -------------------------------------------------------------------------------- /test/test_imports.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import pkgutil 3 | from unittest import TestCase, TestSuite 4 | 5 | from gi.repository import Gdk, GdkX11 6 | 7 | 8 | fake_x11_screen = GdkX11.X11Screen() 9 | Gdk.Screen.get_default = lambda: fake_x11_screen 10 | 11 | 12 | class TestImports(TestCase): 13 | def __init__(self, mod_name): 14 | name = f"test_{mod_name.replace('.', '_')}_import" 15 | 16 | def f(): 17 | __import__(mod_name) 18 | 19 | setattr(self, name, f) 20 | super().__init__(name) 21 | 22 | 23 | def load_tests(*_args): 24 | test_cases = TestSuite() 25 | home = os.path.dirname(os.path.dirname(__file__)) 26 | for package in pkgutil.walk_packages([f"{home}/blueman"], "blueman."): 27 | test_cases.addTest(TestImports(package.name)) 28 | 29 | assert test_cases.countTestCases() > 0 30 | 31 | return test_cases 32 | -------------------------------------------------------------------------------- /blueman/bluez/AgentManager.py: -------------------------------------------------------------------------------- 1 | from blueman.bluemantyping import ObjectPath 2 | 3 | from blueman.bluez.Base import Base 4 | from gi.repository import GLib 5 | 6 | 7 | class AgentManager(Base): 8 | _interface_name = 'org.bluez.AgentManager1' 9 | _obj_path = ObjectPath('/org/bluez') 10 | 11 | def __init__(self) -> None: 12 | super().__init__(obj_path=self._obj_path) 13 | 14 | def register_agent(self, agent_path: str, capability: str = "", default: bool = False) -> None: 15 | param = GLib.Variant('(os)', (agent_path, capability)) 16 | self._call('RegisterAgent', param) 17 | if default: 18 | default_param = GLib.Variant('(o)', (agent_path,)) 19 | self._call('RequestDefaultAgent', default_param) 20 | 21 | def unregister_agent(self, agent_path: str) -> None: 22 | param = GLib.Variant('(o)', (agent_path,)) 23 | self._call('UnregisterAgent', param) 24 | -------------------------------------------------------------------------------- /blueman/services/Functions.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | import logging 3 | from typing import Optional, List 4 | 5 | from blueman.Service import Service 6 | from blueman.Sdp import ServiceUUID 7 | from blueman.bluez.Device import Device 8 | from blueman.bluez.errors import BluezDBusException 9 | import blueman.services 10 | 11 | 12 | def get_service(device: Device, uuid: str) -> Optional[Service]: 13 | for name, cls in inspect.getmembers(blueman.services, inspect.isclass): 14 | if ServiceUUID(uuid).short_uuid == cls.__svclass_id__: 15 | svc: Service = cls(device, uuid) 16 | return svc 17 | return None 18 | 19 | 20 | def get_services(device: Device) -> List[Service]: 21 | try: 22 | services = (get_service(device, uuid) for uuid in device['UUIDs']) 23 | return [service for service in services if service] 24 | except BluezDBusException as e: 25 | logging.exception(e) 26 | return [] 27 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Run this to generate all the initial makefiles, etc. 3 | 4 | srcdir=`dirname $0` 5 | test -z "$srcdir" && srcdir=. 6 | 7 | PROJECT="blueman" 8 | 9 | (test -f $srcdir/configure.ac) || { 10 | echo -n "**Error**: Directory \"\'$srcdir\'\" does not look like the" 11 | echo " top-level package directory" 12 | exit 1 13 | } 14 | 15 | if test -z "$AUTOGEN_SUBDIR_MODE" && test -z "$NOCONFIGURE"; then 16 | if test -z "$*"; then 17 | echo "I am going to run ./configure with no arguments - if you wish " 18 | echo "to pass any to it, please specify them on the $0 command line." 19 | fi 20 | fi 21 | 22 | 23 | autoreconf --verbose --force --install || exit $? 24 | 25 | if test -z "$AUTOGEN_SUBDIR_MODE" && test -z "$NOCONFIGURE"; then 26 | $srcdir/configure --enable-maintainer-mode $AUTOGEN_CONFIGURE_ARGS "$@" || exit $? 27 | 28 | echo 29 | echo "Now type 'make' to compile $PROJECT." 30 | fi 31 | -------------------------------------------------------------------------------- /data/icons/pixmaps/Makefile.am: -------------------------------------------------------------------------------- 1 | pixmapsdir = $(datadir)/blueman/pixmaps 2 | 3 | pixmaps_DATA = \ 4 | blueman-battery-10.png \ 5 | blueman-battery-20.png \ 6 | blueman-battery-30.png \ 7 | blueman-battery-40.png \ 8 | blueman-battery-50.png \ 9 | blueman-battery-60.png \ 10 | blueman-battery-70.png \ 11 | blueman-battery-80.png \ 12 | blueman-battery-90.png \ 13 | blueman-battery-100.png \ 14 | blueman-rssi-10.png \ 15 | blueman-rssi-20.png \ 16 | blueman-rssi-30.png \ 17 | blueman-rssi-40.png \ 18 | blueman-rssi-50.png \ 19 | blueman-rssi-60.png \ 20 | blueman-rssi-70.png \ 21 | blueman-rssi-80.png \ 22 | blueman-rssi-90.png \ 23 | blueman-rssi-100.png \ 24 | blueman-tpl-10.png \ 25 | blueman-tpl-20.png \ 26 | blueman-tpl-30.png \ 27 | blueman-tpl-40.png \ 28 | blueman-tpl-50.png \ 29 | blueman-tpl-60.png \ 30 | blueman-tpl-70.png \ 31 | blueman-tpl-80.png \ 32 | blueman-tpl-90.png \ 33 | blueman-tpl-100.png 34 | 35 | EXTRA_DIST = $(pixmaps_DATA) 36 | -------------------------------------------------------------------------------- /blueman/plugins/mechanism/Rfcomm.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import signal 4 | from blueman.Constants import RFCOMM_WATCHER_PATH 5 | from blueman.plugins.MechanismPlugin import MechanismPlugin 6 | 7 | 8 | class Rfcomm(MechanismPlugin): 9 | def on_load(self) -> None: 10 | self.parent.add_method("OpenRFCOMM", ("n",), "", self._open_rfcomm) 11 | self.parent.add_method("CloseRFCOMM", ("n",), "", self._close_rfcomm) 12 | 13 | def _open_rfcomm(self, port_id: int) -> None: 14 | subprocess.Popen([RFCOMM_WATCHER_PATH, f"/dev/rfcomm{port_id:d}"]) 15 | 16 | def _close_rfcomm(self, port_id: int) -> None: 17 | out, err = subprocess.Popen(['ps', '-e', 'o', 'pid,args'], stdout=subprocess.PIPE).communicate() 18 | for line in out.decode("UTF-8").splitlines(): 19 | pid, cmdline = line.split(maxsplit=1) 20 | if f"blueman-rfcomm-watcher /dev/rfcomm{port_id:d}" in cmdline: 21 | os.kill(int(pid), signal.SIGTERM) 22 | -------------------------------------------------------------------------------- /blueman/plugins/applet/Makefile.am: -------------------------------------------------------------------------------- 1 | bluemandir = $(pythondir)/blueman/plugins/applet 2 | 3 | blueman_PYTHON = \ 4 | __init__.py \ 5 | AuthAgent.py \ 6 | AutoConnect.py \ 7 | ConnectionNotifier.py \ 8 | DBusService.py \ 9 | DhcpClient.py \ 10 | DisconnectItems.py \ 11 | DiscvManager.py \ 12 | ExitItem.py \ 13 | GameControllerWakelock.py \ 14 | KillSwitch.py \ 15 | Menu.py \ 16 | NetUsage.py \ 17 | Networking.py \ 18 | NMDUNSupport.py \ 19 | NMPANSupport.py \ 20 | PowerManager.py \ 21 | PPPSupport.py \ 22 | RecentConns.py \ 23 | SerialManager.py \ 24 | ShowConnected.py \ 25 | StatusIcon.py \ 26 | StandardItems.py \ 27 | StatusNotifierItem.py \ 28 | TransferService.py 29 | 30 | if HAVE_PULSEAUDIO 31 | blueman_PYTHON += PulseAudioProfile.py 32 | endif 33 | 34 | CLEANFILES = \ 35 | $(BUILT_SOURCES) 36 | 37 | DISTCLEANFILES = \ 38 | $(CLEANFILES) 39 | 40 | clean-local: 41 | rm -rf *.pyc *.pyo 42 | 43 | -------------------------------------------------------------------------------- /blueman/Constants.py.in: -------------------------------------------------------------------------------- 1 | __all__ = ["VERSION", "PACKAGE", "WEBSITE", "ICON_PATH", "PIXMAP_PATH", "UI_PATH", "BIN_DIR", "BLUETOOTHD_PATH"] 2 | 3 | VERSION = "@VERSION@" 4 | PACKAGE = "@PACKAGE@" 5 | WEBSITE = "https://github.com/blueman-project/blueman" 6 | PREFIX = "@prefix@" 7 | BIN_DIR = "@BINDIR@" 8 | LOCALEDIR = "@LOCALEDIR@" 9 | ICON_PATH = "@icondir@" 10 | PIXMAP_PATH = "@pkgdatadir@/pixmaps" 11 | BLUETOOTHD_PATH = "@bluetoothd_path@" 12 | UI_PATH = "@pkgdatadir@/ui" 13 | DHCP_CONFIG_FILE = "@dhconfig@" 14 | POLKIT = @POLKIT@ 15 | GETTEXT_PACKAGE = "@GETTEXT_PACKAGE@" 16 | RFCOMM_WATCHER_PATH = "@LIBEXECDIR@/blueman-rfcomm-watcher" 17 | 18 | import os 19 | 20 | if 'BLUEMAN_SOURCE' in os.environ: 21 | _dirname = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) 22 | BIN_DIR = os.path.join(_dirname, 'apps') 23 | ICON_PATH = os.path.join(_dirname, 'data', 'icons') 24 | PIXMAP_PATH = os.path.join(_dirname, 'data', 'icons', 'pixmaps') 25 | UI_PATH = os.path.join(_dirname, 'data', 'ui') 26 | -------------------------------------------------------------------------------- /blueman/main/BatteryWatcher.py: -------------------------------------------------------------------------------- 1 | import weakref 2 | from typing import Callable 3 | from blueman.bluemantyping import ObjectPath 4 | 5 | from blueman.bluez.Battery import Battery, AnyBattery 6 | from blueman.bluez.Manager import Manager 7 | 8 | 9 | class BatteryWatcher: 10 | def __init__(self, callback: Callable[[ObjectPath, int], None]) -> None: 11 | super().__init__() 12 | manager = Manager() 13 | weakref.finalize( 14 | self, 15 | manager.disconnect_signal, 16 | manager.connect_signal( 17 | "battery-created", 18 | lambda _manager, obj_path: callback(obj_path, Battery(obj_path=obj_path)["Percentage"]) 19 | ) 20 | ) 21 | 22 | any_battery = AnyBattery() 23 | weakref.finalize( 24 | self, 25 | any_battery.disconnect_signal, 26 | any_battery.connect_signal( 27 | "property-changed", 28 | lambda _any_battery, key, value, path: callback(path, value) if key == "Percentage" else None 29 | ) 30 | ) 31 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('runtime_deps_check', type: 'boolean', value: true, description: 'Disable runtime dependency check (for package maintainers)') 2 | option('dhcp-config-path', type: 'string', value: '/etc/dhcp3/dhcpd.conf', description: 'Set dhcp3 server configuration path') 3 | option('bluetoothd-path', type: 'string', value: '/usr/libexec/bluetoothd', description: 'Set bluetooth daemon path') 4 | option('policykit', type: 'boolean', value: true, description: 'Enable policykit support') 5 | option('pulseaudio', type: 'boolean', value: true, description: 'Enable PulseAudio support') 6 | option('systemdsystemunitdir', type: 'string', description: 'Path to systemd system unit dir relative to ${prefix}') 7 | option('systemduserunitdir', type: 'string', description: 'Path to systemd user unit dir relative to ${prefix}') 8 | option('sendto-plugins', type: 'array', choices: ['Caja', 'Nemo', 'Nautilus'], value: ['Caja', 'Nemo', 'Nautilus'], description: 'Install sendto plugins for various filemanagers') 9 | option('thunar-sendto', type: 'boolean', value: true, description: 'Install Thunar sendto plugin') 10 | -------------------------------------------------------------------------------- /blueman/config/AutoConnectConfig.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple 2 | from blueman.bluemantyping import BtAddress 3 | import gi 4 | gi.require_version("Gtk", "3.0") 5 | from gi.repository import Gtk 6 | from gi.repository import Gio 7 | 8 | 9 | class AutoConnectConfig(Gio.Settings): 10 | def __init__(self) -> None: 11 | super().__init__(schema_id="org.blueman.plugins.autoconnect") 12 | 13 | def bind_to_menuitem(self, item: Gtk.CheckMenuItem, data: Tuple[BtAddress, str]) -> None: 14 | def switch(active: bool) -> None: 15 | services = set(self["services"]) 16 | if active: 17 | self["services"] = set(services).union({data}) 18 | else: 19 | self["services"] = set(self["services"]).difference({data}) 20 | 21 | def on_change(config: AutoConnectConfig, key: str) -> None: 22 | if key == "services": 23 | item.props.active = data in set(config[key]) 24 | 25 | item.props.active = data in set(self["services"]) 26 | item.connect("toggled", lambda i: switch(i.props.active)) 27 | self.connect("changed", on_change) 28 | -------------------------------------------------------------------------------- /test/plugins/mechanism/test_imports.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import pkgutil 3 | from unittest import TestCase, TestSuite 4 | 5 | 6 | class TestImports(TestCase): 7 | def __init__(self, mod_name, import_error): 8 | name = f"test_{mod_name.replace('.', '_')}_import" 9 | 10 | def run(): 11 | try: 12 | __import__(mod_name) 13 | except ImportError as e: 14 | self.assertIsNotNone(import_error) 15 | self.assertEqual(e.msg, import_error) 16 | 17 | setattr(self, name, run) 18 | super().__init__(name) 19 | 20 | 21 | def load_tests(*_args): 22 | expected_exceptions = { 23 | "blueman.plugins.mechanism.RfKill": "Hardware kill switch not found", 24 | } 25 | 26 | test_cases = TestSuite() 27 | home, subpath = os.path.dirname(__file__).rsplit("/test/", 1) 28 | for package in pkgutil.iter_modules([f"{home}/blueman/{subpath}"], f"blueman.{subpath.replace('/', '.')}."): 29 | test_cases.addTest(TestImports(package.name, expected_exceptions.get(package.name))) 30 | 31 | assert test_cases.countTestCases() > 0 32 | 33 | return test_cases 34 | -------------------------------------------------------------------------------- /test/plugins/manager/test_imports.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import pkgutil 3 | from unittest import TestCase, TestSuite 4 | 5 | 6 | class TestImports(TestCase): 7 | def __init__(self, mod_name, import_error): 8 | name = f"test_{mod_name.replace('.', '_')}_import" 9 | 10 | def run(): 11 | try: 12 | __import__(mod_name) 13 | except ImportError as e: 14 | self.assertIsNotNone(import_error) 15 | self.assertEqual(e.msg, import_error) 16 | 17 | setattr(self, name, run) 18 | super().__init__(name) 19 | 20 | 21 | def load_tests(*_args): 22 | expected_exceptions = { 23 | "blueman.plugins.manager.PulseAudioProfile": "Could not load pulseaudio shared library", 24 | } 25 | 26 | test_cases = TestSuite() 27 | home, subpath = os.path.dirname(__file__).rsplit("/test/", 1) 28 | for package in pkgutil.iter_modules([f"{home}/blueman/{subpath}"], f"blueman.{subpath.replace('/', '.')}."): 29 | test_cases.addTest(TestImports(package.name, expected_exceptions.get(package.name))) 30 | 31 | assert test_cases.countTestCases() > 0 32 | 33 | return test_cases 34 | -------------------------------------------------------------------------------- /module/libblueman.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define ERR_CANNOT_ALLOCATE -1 3 | #define ERR_HCI_DEV_OPEN_FAILED -2 4 | #define ERR_NOT_CONNECTED -3 5 | #define ERR_GET_CONN_INFO_FAILED -4 6 | #define ERR_READ_RSSI_FAILED -5 7 | #define ERR_READ_TPL_FAILED -6 8 | #define ERR_GET_RFCOMM_LIST_FAILED -8 9 | #define ERR_SOCKET_FAILED -9 10 | #define ERR_BIND_FAILED -12 11 | #define ERR_CONNECT_FAILED -13 12 | #define ERR_CREATE_DEV_FAILED -14 13 | #define ERR_RELEASE_DEV_FAILED -15 14 | 15 | struct conn_info_handles { 16 | unsigned int handle; 17 | int dd; 18 | }; 19 | 20 | int connection_init(int dev_id, char *addr, struct conn_info_handles *ci); 21 | int connection_get_rssi(struct conn_info_handles *ci, int8_t *ret_rssi); 22 | int connection_get_tpl(struct conn_info_handles *ci, int8_t *ret_tpl, uint8_t type); 23 | int connection_close(struct conn_info_handles *ci); 24 | int get_rfcomm_channel(uint16_t uuid, char* btd_addr); 25 | int get_rfcomm_list(struct rfcomm_dev_list_req **result); 26 | int create_rfcomm_device(char *local_address, char *remote_address, int channel); 27 | int release_rfcomm_device(int id); 28 | 29 | int _create_bridge(const char* name); 30 | int _destroy_bridge(const char* name); 31 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | apps \ 3 | blueman \ 4 | data \ 5 | module \ 6 | po \ 7 | sendto \ 8 | test 9 | 10 | DIST_DOCS = \ 11 | CHANGELOG.md \ 12 | COPYING \ 13 | FAQ \ 14 | README.md 15 | 16 | EXTRA_DIST = \ 17 | ${DIST_DOCS} \ 18 | meson.build \ 19 | meson_options.txt \ 20 | po/meson.build 21 | 22 | MAINTAINERCLEANFILES = \ 23 | Makefile.in \ 24 | aclocal.m4 \ 25 | configure \ 26 | config.h.in \ 27 | config.rpath \ 28 | depcomp \ 29 | missing \ 30 | install-sh \ 31 | ABOUT-NLS \ 32 | config.rpath \ 33 | po/Makefile.in.in \ 34 | po/Makefile.in \ 35 | po/Makefile \ 36 | po/Makevars.template \ 37 | po/quot.sed \ 38 | po/remove-potcdate.sin \ 39 | po/Rules-quot \ 40 | po/blueman.pot \ 41 | po/boldquot.sed \ 42 | po/*.header \ 43 | po/stamp-po \ 44 | po/insert-header.sin \ 45 | $NULL 46 | 47 | doc_DATA = ${DIST_DOCS} 48 | 49 | ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} 50 | 51 | # distcheck fails trying to install to the live filesystem 52 | AM_DISTCHECK_CONFIGURE_FLAGS = \ 53 | --with-systemdsystemunitdir='$$(prefix)/lib/systemd/system' \ 54 | --with-systemduserunitdir='$$(prefix)/lib/systemd/user' 55 | -------------------------------------------------------------------------------- /apps/blueman-tray.in: -------------------------------------------------------------------------------- 1 | #!@PYTHON@ 2 | import logging 3 | import os 4 | import sys 5 | import gettext 6 | 7 | # support running uninstalled 8 | _dirname = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) 9 | if 'BLUEMAN_SOURCE' in os.environ: 10 | sys.path.insert(0, _dirname) 11 | 12 | gettext.textdomain("@GETTEXT_PACKAGE@") 13 | 14 | from blueman.Functions import set_proc_title, create_parser, create_logger 15 | from blueman.main.Tray import BluemanTray 16 | 17 | 18 | if __name__ == '__main__': 19 | parser = create_parser() 20 | args = parser.parse_args() 21 | 22 | if args.LEVEL.upper() == "DEBUG": 23 | log_level = logging.DEBUG 24 | elif args.LEVEL.upper() == "INFO": 25 | log_level = logging.INFO 26 | elif args.LEVEL.upper() == "WARNING": 27 | log_level = logging.WARNING 28 | elif args.LEVEL.upper() == "ERROR": 29 | log_level = logging.ERROR 30 | elif args.LEVEL.upper() == "CRITICAL": 31 | log_level = logging.CRITICAL 32 | else: 33 | log_level = logging.WARNING 34 | 35 | create_logger(log_level, "blueman-tray", syslog=args.syslog) 36 | 37 | set_proc_title() 38 | app = BluemanTray() 39 | app.run() 40 | -------------------------------------------------------------------------------- /blueman/plugins/mechanism/Ppp.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | from blueman.main.PPPConnection import PPPConnection 4 | from blueman.plugins.MechanismPlugin import MechanismPlugin 5 | 6 | 7 | class Ppp(MechanismPlugin): 8 | def on_load(self) -> None: 9 | self.parent.add_method("PPPConnect", ("u", "s", "s"), "s", self._ppp_connect, pass_sender=True, is_async=True) 10 | 11 | def _ppp_connected(self, _ppp: PPPConnection, port: str, ok: Callable[[str], None]) -> None: 12 | ok(port) 13 | self.timer.resume() 14 | 15 | def _ppp_error(self, _ppp: PPPConnection, message: str, err: Callable[[str], None]) -> None: 16 | err(message) 17 | self.timer.resume() 18 | 19 | def _ppp_connect(self, port: int, number: str, apn: str, caller: str, 20 | ok: Callable[[str], None], err: Callable[[str], None]) -> None: 21 | self.confirm_authorization(caller, "org.blueman.pppd.pppconnect") 22 | self.timer.stop() 23 | 24 | ppp = PPPConnection(f"/dev/rfcomm{port:d}", number, apn) 25 | ppp.connect("error-occurred", self._ppp_error, err) 26 | ppp.connect("connected", self._ppp_connected, ok) 27 | 28 | ppp.connect_rfcomm() 29 | -------------------------------------------------------------------------------- /test/main/test_imports.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import pkgutil 3 | from unittest import TestCase, TestSuite 4 | 5 | 6 | class TestImports(TestCase): 7 | def __init__(self, mod_name, import_error): 8 | name = f"test_{mod_name.replace('.', '_')}_import" 9 | 10 | def run(): 11 | try: 12 | __import__(mod_name) 13 | except ImportError as e: 14 | self.assertIsNotNone(import_error) 15 | self.assertEqual(e.msg, import_error) 16 | 17 | setattr(self, name, run) 18 | super().__init__(name) 19 | 20 | 21 | def load_tests(*_args): 22 | expected_exceptions = { 23 | "blueman.main.NetworkManager": "NM python bindings not found.", 24 | "blueman.main.PulseAudioUtils": "Could not load pulseaudio shared library", 25 | } 26 | 27 | test_cases = TestSuite() 28 | home, subpath = os.path.dirname(__file__).rsplit("/test/", 1) 29 | for package in pkgutil.iter_modules([f"{home}/blueman/{subpath}"], f"blueman.{subpath.replace('/', '.')}."): 30 | test_cases.addTest(TestImports(package.name, expected_exceptions.get(package.name))) 31 | 32 | assert test_cases.countTestCases() > 0 33 | 34 | return test_cases 35 | -------------------------------------------------------------------------------- /blueman/main/SpeedCalc.py: -------------------------------------------------------------------------------- 1 | import time 2 | from typing import List, Tuple 3 | 4 | 5 | class SpeedCalc: 6 | def __init__(self, moving_avg: float = 3.0) -> None: 7 | self.moving_avg = moving_avg 8 | self.log: List[Tuple[float, float]] = [] 9 | 10 | self.reference: float = 0 11 | 12 | def calc(self, amount: float) -> float: 13 | if not self.log: 14 | self.reference = amount 15 | amount -= self.reference 16 | curtime = round(time.time(), 2) 17 | self.log.append((curtime, amount)) 18 | if len(self.log) >= 2: 19 | total_time = self.log[-1][0] - self.log[0][0] 20 | # print "tt "+str(total_time) 21 | if total_time >= self.moving_avg: 22 | total_amount = self.log[-1][1] - self.log[0][1] 23 | 24 | speed = total_amount / total_time 25 | del self.log[0] 26 | return speed 27 | 28 | else: 29 | total_amount = self.log[-1][1] - self.log[0][1] 30 | speed = total_amount / total_time 31 | return speed 32 | 33 | else: 34 | return 0 35 | 36 | def reset(self) -> None: 37 | self.log = [] 38 | -------------------------------------------------------------------------------- /stubs/gi/repository/GModule.pyi: -------------------------------------------------------------------------------- 1 | import builtins 2 | import typing 3 | 4 | from gi.repository import GLib 5 | 6 | 7 | class Module(): 8 | 9 | @staticmethod 10 | def build_path(directory: typing.Optional[builtins.str], module_name: builtins.str) -> builtins.str: ... 11 | 12 | def close(self) -> builtins.bool: ... 13 | 14 | @staticmethod 15 | def error() -> builtins.str: ... 16 | 17 | def make_resident(self) -> None: ... 18 | 19 | def name(self) -> builtins.str: ... 20 | 21 | @staticmethod 22 | def supported() -> builtins.bool: ... 23 | 24 | def symbol(self, symbol_name: builtins.str) -> typing.Tuple[builtins.bool, builtins.object]: ... 25 | 26 | 27 | class ModuleFlags(GLib.Flags, builtins.int): 28 | LAZY = ... # type: ModuleFlags 29 | LOCAL = ... # type: ModuleFlags 30 | MASK = ... # type: ModuleFlags 31 | 32 | 33 | ModuleCheckInit = typing.Callable[[Module], builtins.str] 34 | ModuleUnload = typing.Callable[[Module], None] 35 | 36 | 37 | def module_build_path(directory: typing.Optional[builtins.str], module_name: builtins.str) -> builtins.str: ... 38 | 39 | 40 | def module_error() -> builtins.str: ... 41 | 42 | 43 | def module_supported() -> builtins.bool: ... 44 | 45 | 46 | -------------------------------------------------------------------------------- /blueman/bluez/Adapter.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Callable 2 | from blueman.bluemantyping import ObjectPath 3 | 4 | from gi.repository import GLib 5 | 6 | from blueman.bluez.AnyBase import AnyBase 7 | from blueman.bluez.Base import Base 8 | from blueman.bluez.Device import Device 9 | from blueman.bluez.errors import BluezDBusException 10 | 11 | 12 | class Adapter(Base): 13 | _interface_name = 'org.bluez.Adapter1' 14 | 15 | def __init__(self, obj_path: ObjectPath): 16 | super().__init__(obj_path=obj_path) 17 | 18 | def start_discovery(self, error_handler: Optional[Callable[[BluezDBusException], None]] = None) -> None: 19 | self._call('StartDiscovery', error_handler=error_handler) 20 | 21 | def stop_discovery(self) -> None: 22 | self._call('StopDiscovery') 23 | 24 | def remove_device(self, device: Device) -> None: 25 | param = GLib.Variant('(o)', (device.get_object_path(),)) 26 | self._call('RemoveDevice', param) 27 | 28 | def get_name(self) -> str: 29 | name: str = self['Alias'] 30 | return name 31 | 32 | def set_name(self, name: str) -> None: 33 | self.set('Alias', name) 34 | 35 | 36 | class AnyAdapter(AnyBase): 37 | def __init__(self) -> None: 38 | super().__init__('org.bluez.Adapter1') 39 | -------------------------------------------------------------------------------- /apps/blueman-manager.in: -------------------------------------------------------------------------------- 1 | #!@PYTHON@ 2 | import sys 3 | import os 4 | import logging 5 | import gettext 6 | 7 | # support running uninstalled 8 | _dirname = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) 9 | if 'BLUEMAN_SOURCE' in os.environ: 10 | sys.path = [_dirname, os.path.join(_dirname, 'module', '.libs')] + sys.path 11 | os.environ["GSETTINGS_SCHEMA_DIR"] = os.path.join(_dirname, "data") 12 | 13 | gettext.textdomain("@GETTEXT_PACKAGE@") 14 | 15 | from blueman.main.Manager import Blueman 16 | from blueman.Functions import set_proc_title, create_parser, create_logger 17 | 18 | 19 | if __name__ == '__main__': 20 | parser = create_parser() 21 | args = parser.parse_args() 22 | 23 | if args.LEVEL.upper() == "DEBUG": 24 | log_level = logging.DEBUG 25 | elif args.LEVEL.upper() == "INFO": 26 | log_level = logging.INFO 27 | elif args.LEVEL.upper() == "WARNING": 28 | log_level = logging.WARNING 29 | elif args.LEVEL.upper() == "ERROR": 30 | log_level = logging.ERROR 31 | elif args.LEVEL.upper() == "CRITICAL": 32 | log_level = logging.CRITICAL 33 | else: 34 | log_level = logging.WARNING 35 | 36 | create_logger(log_level, "blueman-manager", syslog=args.syslog) 37 | 38 | app = Blueman() 39 | set_proc_title() 40 | app.run() 41 | -------------------------------------------------------------------------------- /blueman/bluez/Network.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Callable 2 | from blueman.bluemantyping import ObjectPath 3 | 4 | from blueman.bluez.Base import Base 5 | from blueman.bluez.AnyBase import AnyBase 6 | from gi.repository import GLib 7 | 8 | from blueman.bluez.errors import BluezDBusException 9 | 10 | 11 | class Network(Base): 12 | _interface_name = 'org.bluez.Network1' 13 | 14 | def __init__(self, obj_path: ObjectPath): 15 | super().__init__(obj_path=obj_path) 16 | 17 | def connect( # type: ignore 18 | self, 19 | uuid: str, 20 | reply_handler: Optional[Callable[[str], None]] = None, 21 | error_handler: Optional[Callable[[BluezDBusException], None]] = None, 22 | ) -> None: 23 | param = GLib.Variant('(s)', (uuid,)) 24 | self._call('Connect', param, reply_handler=reply_handler, error_handler=error_handler) 25 | 26 | def disconnect( # type: ignore 27 | self, 28 | reply_handler: Optional[Callable[[], None]] = None, 29 | error_handler: Optional[Callable[[BluezDBusException], None]] = None, 30 | ) -> None: 31 | self._call('Disconnect', reply_handler=reply_handler, error_handler=error_handler) 32 | 33 | 34 | class AnyNetwork(AnyBase): 35 | def __init__(self) -> None: 36 | super().__init__('org.bluez.Network1') 37 | -------------------------------------------------------------------------------- /apps/blueman-applet.in: -------------------------------------------------------------------------------- 1 | #!@PYTHON@ 2 | import sys 3 | import os 4 | import logging 5 | import gettext 6 | 7 | # support running uninstalled 8 | _dirname = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) 9 | if 'BLUEMAN_SOURCE' in os.environ: 10 | sys.path = [_dirname, os.path.join(_dirname, 'module', '.libs')] + sys.path 11 | os.environ["GSETTINGS_SCHEMA_DIR"] = os.path.join(_dirname, "data") 12 | 13 | gettext.textdomain("@GETTEXT_PACKAGE@") 14 | 15 | from blueman.Functions import create_logger, create_parser, set_proc_title 16 | from blueman.main.Applet import BluemanApplet 17 | 18 | 19 | if __name__ == '__main__': 20 | parser = create_parser() 21 | args = parser.parse_args() 22 | 23 | if args.LEVEL.upper() == "DEBUG": 24 | log_level = logging.DEBUG 25 | elif args.LEVEL.upper() == "INFO": 26 | log_level = logging.INFO 27 | elif args.LEVEL.upper() == "WARNING": 28 | log_level = logging.WARNING 29 | elif args.LEVEL.upper() == "ERROR": 30 | log_level = logging.ERROR 31 | elif args.LEVEL.upper() == "CRITICAL": 32 | log_level = logging.CRITICAL 33 | else: 34 | log_level = logging.WARNING 35 | 36 | create_logger(log_level, "blueman-applet", syslog=args.syslog) 37 | 38 | set_proc_title() 39 | app = BluemanApplet() 40 | app.run() 41 | -------------------------------------------------------------------------------- /blueman/bluez/obex/AgentManager.py: -------------------------------------------------------------------------------- 1 | from blueman.bluemantyping import ObjectPath 2 | import logging 3 | 4 | from blueman.bluez.errors import BluezDBusException 5 | from blueman.bluez.obex.Base import Base 6 | from gi.repository import GLib 7 | 8 | 9 | class AgentManager(Base): 10 | _interface_name = 'org.bluez.obex.AgentManager1' 11 | _obj_path: ObjectPath = ObjectPath('/org/bluez/obex') 12 | 13 | def __init__(self) -> None: 14 | super().__init__(obj_path=self._obj_path) 15 | 16 | def register_agent(self, agent_path: str) -> None: 17 | def on_registered() -> None: 18 | logging.info(agent_path) 19 | 20 | def on_register_failed(error: BluezDBusException) -> None: 21 | logging.error(f"{agent_path} {error}") 22 | 23 | param = GLib.Variant('(o)', (agent_path,)) 24 | self._call('RegisterAgent', param, reply_handler=on_registered, error_handler=on_register_failed) 25 | 26 | def unregister_agent(self, agent_path: ObjectPath) -> None: 27 | def on_unregistered() -> None: 28 | logging.info(agent_path) 29 | 30 | def on_unregister_failed(error: BluezDBusException) -> None: 31 | logging.error(f"{agent_path} {error}") 32 | 33 | param = GLib.Variant('(o)', (agent_path,)) 34 | self._call('UnregisterAgent', param, reply_handler=on_unregistered, error_handler=on_unregister_failed) 35 | -------------------------------------------------------------------------------- /blueman/gui/Animation.py: -------------------------------------------------------------------------------- 1 | from typing import Iterable, Optional 2 | 3 | from gi import require_version 4 | require_version("Gtk", "3.0") 5 | from gi.repository import GLib, Gtk 6 | 7 | 8 | class Animation: 9 | def __init__(self, icon: Gtk.Image, icons: Iterable[str], rate: int = 1) -> None: 10 | self.icon_names = list(icons) 11 | self.timer: Optional[int] = None 12 | self.current = 0 13 | self.icon = icon 14 | self.rate = int(1000 / rate) 15 | 16 | def status(self) -> bool: 17 | if self.timer: 18 | return True 19 | else: 20 | return False 21 | 22 | def set_rate(self, rate: float) -> None: 23 | if not self.rate == int(1000 / rate): 24 | self.rate = int(1000 / rate) 25 | self.stop() 26 | self.start() 27 | 28 | def start(self) -> None: 29 | self.timer = GLib.timeout_add(self.rate, self._animation) 30 | 31 | def stop(self) -> None: 32 | if self.timer: 33 | GLib.source_remove(self.timer) 34 | self.icon.props.icon_name = self.icon_names[0] 35 | self.timer = None 36 | 37 | def _animation(self) -> bool: 38 | self.current += 1 39 | if self.current > (len(self.icon_names) - 1): 40 | self.current = 0 41 | self.icon.props.icon_name = self.icon_names[self.current] 42 | 43 | return True 44 | -------------------------------------------------------------------------------- /blueman/gui/GsmSettings.py: -------------------------------------------------------------------------------- 1 | from gettext import gettext as _ 2 | 3 | from blueman.main.Builder import Builder 4 | 5 | import gi 6 | gi.require_version("Gtk", "3.0") 7 | from gi.repository import Gtk 8 | from gi.repository import Gio 9 | 10 | 11 | class GsmSettings(Gtk.Dialog): 12 | def __init__(self, bd_address: str) -> None: 13 | super().__init__() 14 | 15 | self.set_name("GsmSettings") 16 | self.device = bd_address 17 | 18 | builder = Builder("gsm-settings.ui") 19 | 20 | gsm_grid = builder.get_widget("gsm_grid", Gtk.Grid) 21 | 22 | self.config = Gio.Settings(schema_id="org.blueman.gsmsetting", 23 | path=f"/org/blueman/gsmsettings/{bd_address}/") 24 | self.props.icon_name = "network-wireless-symbolic" 25 | self.props.title = _("GSM Settings") 26 | 27 | self.props.resizable = False 28 | 29 | a = self.get_content_area() 30 | a.pack_start(gsm_grid, True, True, 0) 31 | gsm_grid.show() 32 | 33 | self.e_apn = builder.get_widget("e_apn", Gtk.Entry) 34 | self.e_number = builder.get_widget("e_number", Gtk.Entry) 35 | 36 | self.config.bind("apn", self.e_apn, "text", Gio.SettingsBindFlags.DEFAULT) 37 | self.config.bind("number", self.e_number, "text", Gio.SettingsBindFlags.DEFAULT) 38 | 39 | self.add_button(_("_Close"), Gtk.ResponseType.CLOSE) 40 | -------------------------------------------------------------------------------- /test/plugins/applet/test_imports.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import pkgutil 3 | from unittest import TestCase, TestSuite 4 | 5 | 6 | class TestImports(TestCase): 7 | def __init__(self, mod_name, import_error): 8 | name = f"test_{mod_name.replace('.', '_')}_import" 9 | 10 | def run(): 11 | try: 12 | __import__(mod_name) 13 | except ImportError as e: 14 | self.assertIsNotNone(import_error) 15 | self.assertEqual(e.msg, import_error) 16 | 17 | setattr(self, name, run) 18 | super().__init__(name) 19 | 20 | 21 | def load_tests(*_args): 22 | expected_exceptions = { 23 | "blueman.plugins.applet.GameControllerWakelock": "This is not an X11 screen", 24 | "blueman.plugins.applet.KillSwitch": "Hardware kill switch not found", 25 | "blueman.plugins.applet.NMDUNSupport": "NM python bindings not found.", 26 | "blueman.plugins.applet.NMPANSupport": "NM python bindings not found.", 27 | } 28 | 29 | test_cases = TestSuite() 30 | home, subpath = os.path.dirname(__file__).rsplit("/test/", 1) 31 | for package in pkgutil.iter_modules([f"{home}/blueman/{subpath}"], f"blueman.{subpath.replace('/', '.')}."): 32 | test_cases.addTest(TestImports(package.name, expected_exceptions.get(package.name))) 33 | 34 | assert test_cases.countTestCases() > 0 35 | 36 | return test_cases 37 | -------------------------------------------------------------------------------- /apps/blueman-services.in: -------------------------------------------------------------------------------- 1 | #!@PYTHON@ 2 | 3 | import os 4 | import sys 5 | import logging 6 | import signal 7 | import gettext 8 | 9 | # support running uninstalled 10 | _dirname = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) 11 | if 'BLUEMAN_SOURCE' in os.environ: 12 | sys.path = [_dirname, os.path.join(_dirname, 'module', '.libs')] + sys.path 13 | os.environ["GSETTINGS_SCHEMA_DIR"] = os.path.join(_dirname, "data") 14 | 15 | _ = gettext.gettext 16 | gettext.textdomain("@GETTEXT_PACKAGE@") 17 | 18 | from blueman.Functions import set_proc_title, setup_icon_path, create_logger, create_parser 19 | from blueman.main.Services import BluemanServices 20 | 21 | 22 | if __name__ == '__main__': 23 | parser = create_parser() 24 | args = parser.parse_args() 25 | 26 | if args.LEVEL.upper() == "DEBUG": 27 | log_level = logging.DEBUG 28 | elif args.LEVEL.upper() == "INFO": 29 | log_level = logging.INFO 30 | elif args.LEVEL.upper() == "WARNING": 31 | log_level = logging.WARNING 32 | elif args.LEVEL.upper() == "ERROR": 33 | log_level = logging.ERROR 34 | elif args.LEVEL.upper() == "CRITICAL": 35 | log_level = logging.CRITICAL 36 | else: 37 | log_level = logging.WARNING 38 | 39 | create_logger(log_level, "blueman-services", syslog=args.syslog) 40 | 41 | setup_icon_path() 42 | set_proc_title() 43 | app = BluemanServices() 44 | app.run() 45 | -------------------------------------------------------------------------------- /blueman/bluez/obex/ObjectPush.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import Dict 3 | from blueman.bluemantyping import ObjectPath 4 | 5 | from blueman.bluez.errors import BluezDBusException 6 | from blueman.bluez.obex.Base import Base 7 | from gi.repository import GObject, GLib 8 | 9 | from blueman.bluemantyping import GSignals 10 | 11 | 12 | class ObjectPush(Base): 13 | __gsignals__: GSignals = { 14 | 'transfer-started': (GObject.SignalFlags.NO_HOOKS, None, (str, str,)), 15 | 'transfer-failed': (GObject.SignalFlags.NO_HOOKS, None, (str,)), 16 | } 17 | 18 | _interface_name = 'org.bluez.obex.ObjectPush1' 19 | 20 | def __init__(self, obj_path: ObjectPath): 21 | super().__init__(obj_path=obj_path) 22 | 23 | def send_file(self, file_path: str) -> None: 24 | def on_transfer_started(transfer_path: ObjectPath, props: Dict[str, str]) -> None: 25 | logging.info(" ".join((self.get_object_path(), file_path, transfer_path))) 26 | self.emit('transfer-started', transfer_path, props['Filename']) 27 | 28 | def on_transfer_error(error: BluezDBusException) -> None: 29 | logging.error(f"{file_path} {error}") 30 | self.emit('transfer-failed', error) 31 | 32 | param = GLib.Variant('(s)', (file_path,)) 33 | self._call('SendFile', param, reply_handler=on_transfer_started, error_handler=on_transfer_error) 34 | 35 | def get_session_path(self) -> ObjectPath: 36 | path: ObjectPath = self.get_object_path() 37 | return path 38 | -------------------------------------------------------------------------------- /apps/blueman-adapters.in: -------------------------------------------------------------------------------- 1 | #!@PYTHON@ 2 | import os 3 | import sys 4 | import logging 5 | import gettext 6 | 7 | 8 | # support running uninstalled 9 | _dirname = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) 10 | if 'BLUEMAN_SOURCE' in os.environ: 11 | sys.path = [_dirname, os.path.join(_dirname, 'module', '.libs')] + sys.path 12 | os.environ["GSETTINGS_SCHEMA_DIR"] = os.path.join(_dirname, "data") 13 | 14 | gettext.textdomain("@GETTEXT_PACKAGE@") 15 | 16 | from blueman.Functions import create_parser, create_logger, set_proc_title 17 | from blueman.main.Adapter import BluemanAdapters 18 | 19 | 20 | if __name__ == '__main__': 21 | parser = parser = create_parser() 22 | parser.add_argument("--socket-id", dest="socket_id", action="store", type=int, metavar="ID") 23 | parser.add_argument("adapter", nargs="?", metavar="ADAPTER NAME") 24 | args = parser.parse_args() 25 | 26 | if args.LEVEL.upper() == "DEBUG": 27 | log_level = logging.DEBUG 28 | elif args.LEVEL.upper() == "INFO": 29 | log_level = logging.INFO 30 | elif args.LEVEL.upper() == "WARNING": 31 | log_level = logging.WARNING 32 | elif args.LEVEL.upper() == "ERROR": 33 | log_level = logging.ERROR 34 | elif args.LEVEL.upper() == "CRITICAL": 35 | log_level = logging.CRITICAL 36 | else: 37 | log_level = logging.WARNING 38 | 39 | create_logger(log_level, "blueman-adapters", syslog=args.syslog) 40 | 41 | set_proc_title() 42 | 43 | app = blueman_adapters = BluemanAdapters(args.adapter, args.socket_id) 44 | app.run() 45 | -------------------------------------------------------------------------------- /blueman/plugins/applet/NMDUNSupport.py: -------------------------------------------------------------------------------- 1 | from gettext import gettext as _ 2 | from typing import Callable, Union 3 | 4 | from gi.repository import GLib 5 | 6 | from blueman.Service import Service 7 | from blueman.plugins.AppletPlugin import AppletPlugin 8 | from blueman.main.NetworkManager import NMDUNConnection, NMConnectionError 9 | from blueman.Sdp import DIALUP_NET_SVCLASS_ID 10 | from blueman.plugins.applet.DBusService import ServiceConnectHandler 11 | 12 | 13 | class NMDUNSupport(AppletPlugin, ServiceConnectHandler): 14 | __depends__ = ["DBusService"] 15 | __conflicts__ = ["PPPSupport"] 16 | __icon__ = "modem-symbolic" 17 | __author__ = "infirit" 18 | __description__ = _("Provides support for Dial Up Networking (DUN) with ModemManager and NetworkManager") 19 | __priority__ = 1 20 | 21 | def service_connect_handler(self, service: Service, ok: Callable[[], None], 22 | err: Callable[[Union[NMConnectionError, GLib.Error]], None]) -> bool: 23 | if DIALUP_NET_SVCLASS_ID != service.short_uuid: 24 | return False 25 | 26 | conn = NMDUNConnection(service, ok, err) 27 | conn.activate() 28 | 29 | return True 30 | 31 | def service_disconnect_handler(self, service: Service, ok: Callable[[], None], 32 | err: Callable[[Union[NMConnectionError, GLib.Error]], None]) -> bool: 33 | if DIALUP_NET_SVCLASS_ID != service.short_uuid: 34 | return False 35 | 36 | conn = NMDUNConnection(service, ok, err) 37 | conn.deactivate() 38 | 39 | return True 40 | -------------------------------------------------------------------------------- /blueman/plugins/applet/NMPANSupport.py: -------------------------------------------------------------------------------- 1 | from gettext import gettext as _ 2 | from typing import Callable, Union 3 | 4 | from gi.repository import GLib 5 | 6 | from blueman.Service import Service 7 | from blueman.plugins.AppletPlugin import AppletPlugin 8 | from blueman.main.NetworkManager import NMPANConnection, NMConnectionError 9 | from blueman.plugins.applet.DBusService import ServiceConnectHandler 10 | from blueman.services.meta import NetworkService 11 | 12 | 13 | class NMPANSupport(AppletPlugin, ServiceConnectHandler): 14 | __depends__ = ["DBusService"] 15 | __conflicts__ = ["DhcpClient"] 16 | __icon__ = "network-workgroup-symbolic" 17 | __author__ = "infirit" 18 | __description__ = _("Provides support for Personal Area Networking (PAN) introduced in NetworkManager 0.8") 19 | __priority__ = 2 20 | 21 | def service_connect_handler(self, service: Service, ok: Callable[[], None], 22 | err: Callable[[Union[NMConnectionError, GLib.Error]], None]) -> bool: 23 | if not isinstance(service, NetworkService): 24 | return False 25 | 26 | conn = NMPANConnection(service, ok, err) 27 | conn.activate() 28 | 29 | return True 30 | 31 | def service_disconnect_handler(self, service: Service, ok: Callable[[], None], 32 | err: Callable[[Union[NMConnectionError, GLib.Error]], None]) -> bool: 33 | if not isinstance(service, NetworkService): 34 | return False 35 | 36 | conn = NMPANConnection(service, ok, err) 37 | conn.deactivate() 38 | 39 | return True 40 | -------------------------------------------------------------------------------- /blueman/bluez/Device.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Callable 2 | from blueman.bluemantyping import ObjectPath 3 | 4 | from blueman.bluez.Base import Base 5 | from blueman.bluez.AnyBase import AnyBase 6 | from blueman.bluez.errors import BluezDBusException 7 | 8 | 9 | class Device(Base): 10 | _interface_name = 'org.bluez.Device1' 11 | 12 | def __init__(self, obj_path: ObjectPath): 13 | super().__init__(obj_path=obj_path) 14 | 15 | def pair( 16 | self, 17 | reply_handler: Optional[Callable[[], None]] = None, 18 | error_handler: Optional[Callable[[BluezDBusException], None]] = None, 19 | ) -> None: 20 | self._call('Pair', reply_handler=reply_handler, error_handler=error_handler) 21 | 22 | def connect( # type: ignore 23 | self, 24 | reply_handler: Optional[Callable[[], None]] = None, 25 | error_handler: Optional[Callable[[BluezDBusException], None]] = None, 26 | ) -> None: 27 | self._call('Connect', reply_handler=reply_handler, error_handler=error_handler) 28 | 29 | def disconnect( # type: ignore 30 | self, 31 | reply_handler: Optional[Callable[[], None]] = None, 32 | error_handler: Optional[Callable[[BluezDBusException], None]] = None, 33 | ) -> None: 34 | self._call('Disconnect', reply_handler=reply_handler, error_handler=error_handler) 35 | 36 | @property 37 | def display_name(self) -> str: 38 | alias: str = self["Alias"] 39 | return alias.strip() 40 | 41 | 42 | class AnyDevice(AnyBase): 43 | def __init__(self) -> None: 44 | super().__init__('org.bluez.Device1') 45 | -------------------------------------------------------------------------------- /blueman/bluez/AnyBase.py: -------------------------------------------------------------------------------- 1 | import weakref 2 | 3 | from gi.repository import GObject, GLib 4 | from gi.repository import Gio 5 | 6 | from blueman.bluemantyping import GSignals 7 | 8 | 9 | class AnyBase(GObject.GObject): 10 | __gsignals__: GSignals = { 11 | 'property-changed': (GObject.SignalFlags.NO_HOOKS, None, (str, object, str)) 12 | } 13 | 14 | connect_signal = GObject.GObject.connect 15 | disconnect_signal = GObject.GObject.disconnect 16 | 17 | def __init__(self, interface_name: str): 18 | super().__init__() 19 | 20 | bus = Gio.bus_get_sync(Gio.BusType.SYSTEM) 21 | 22 | this = weakref.proxy(self) 23 | 24 | def on_signal( 25 | _connection: Gio.DBusConnection, 26 | _sender_name: str, 27 | object_path: str, 28 | _interface_name: str, 29 | _signal_name: str, 30 | param: GLib.Variant, 31 | ) -> None: 32 | iface_name, changed, invalidated = param.unpack() 33 | if iface_name == interface_name and this is not None: 34 | for key in list(changed) + invalidated: 35 | this.emit('property-changed', key, changed.get(key, None), object_path) 36 | 37 | weakref.finalize( 38 | self, 39 | bus.signal_unsubscribe, 40 | bus.signal_subscribe( 41 | "org.bluez", 42 | "org.freedesktop.DBus.Properties", 43 | "PropertiesChanged", 44 | None, 45 | None, 46 | Gio.DBusSignalFlags.NONE, 47 | on_signal 48 | ) 49 | ) 50 | -------------------------------------------------------------------------------- /blueman/plugins/applet/DisconnectItems.py: -------------------------------------------------------------------------------- 1 | from gettext import gettext as _ 2 | from typing import Any, TYPE_CHECKING, cast, Callable 3 | 4 | from blueman.plugins.AppletPlugin import AppletPlugin 5 | 6 | if TYPE_CHECKING: 7 | from blueman.main.Applet import BluemanApplet 8 | 9 | 10 | class DisconnectItems(AppletPlugin): 11 | __depends__ = ["Menu"] 12 | __icon__ = "bluetooth-disconnected-symbolic" 13 | __author__ = "cschramm" 14 | __description__ = _("Adds disconnect menu items") 15 | 16 | def __init__(self, parent: "BluemanApplet"): 17 | super().__init__(parent) 18 | self._menu = self.parent.Plugins.Menu 19 | 20 | def on_unload(self) -> None: 21 | self._menu.unregister(self) 22 | 23 | def on_manager_state_changed(self, state: bool) -> None: 24 | self._menu.unregister(self) 25 | if state: 26 | self._render() 27 | 28 | def on_adapter_removed(self, path: str) -> None: 29 | self._menu.unregister(self) 30 | self._render() 31 | 32 | def on_device_property_changed(self, path: str, key: str, value: Any) -> None: 33 | if key == "Connected": 34 | self._menu.unregister(self) 35 | self._render() 36 | 37 | def _render(self) -> None: 38 | for idx, device in enumerate(self.parent.Manager.get_devices()): 39 | if device["Connected"]: 40 | self._menu.add(self, (25, idx), text=_("Disconnect %s") % device.display_name, 41 | icon_name="bluetooth-disconnected-symbolic", 42 | callback=cast(Callable[[], None], lambda dev=device: dev.disconnect())) 43 | -------------------------------------------------------------------------------- /stubs/_blueman.pyi: -------------------------------------------------------------------------------- 1 | from typing import List, Dict, Optional 2 | from typing_extensions import TypedDict 3 | 4 | ERR: Dict[int, str] 5 | RFCOMM_HANGUP_NOW: int 6 | RFCOMM_RELEASE_ONHUP: int 7 | RFCOMM_REUSE_DLC: int 8 | RFCOMM_STATES: List[str] 9 | RFCOMM_TTY_ATTACHED: int 10 | 11 | class _RfcommDev(TypedDict): 12 | id: int 13 | channel: int 14 | flags: int 15 | state: str 16 | src: str 17 | dst: str 18 | 19 | class _HciInfo(TypedDict): 20 | acl_mtu: int 21 | acl_pkts: int 22 | bdaddr: str 23 | dev_id: int 24 | features: List[int] 25 | flags: int 26 | link_mode: int 27 | link_policy: int 28 | name: str 29 | pkt_type: int 30 | sco_mtu: int 31 | stat: Dict[str, int] 32 | type: int 33 | 34 | class BridgeException(Exception): 35 | errno: int 36 | def __init__(self, errno: int) -> None: ... 37 | 38 | class ConnInfoReadError(Exception): ... 39 | 40 | class RFCOMMError(Exception): ... 41 | 42 | class conn_info: 43 | failed: bool 44 | def __init__(self, addr: str, hci_name: str) -> None: ... 45 | def deinit(self) -> None: ... 46 | def get_rssi(self) -> int: ... 47 | def get_tpl(self) -> int: ... 48 | def init(self) -> None: ... 49 | 50 | def create_bridge(name: str = "pan1") -> None: ... 51 | def create_rfcomm_device(local_address: str, remote_address: str, channel: int) -> int: ... 52 | def destroy_bridge(name: str = "pan1") -> None: ... 53 | def device_info(hci_name: str = "hci0") -> _HciInfo: ... 54 | def get_rfcomm_channel(uuid: int, bdaddr: str) -> Optional[int]: ... 55 | def release_rfcomm_device(id: int) -> int: ... 56 | def rfcomm_list() -> List[_RfcommDev]: ... 57 | -------------------------------------------------------------------------------- /data/configs/org.blueman.policy.in: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | The Blueman Project 7 | https://github.com/blueman-project/blueman 8 | blueman 9 | 10 | Configure Bluetooth Network 11 | Configuring networking requires privileges 12 | 13 | no 14 | auth_admin_keep 15 | 16 | 17 | 18 | 19 | Launch DHCP client 20 | Launching DHCP client requires privileges 21 | 22 | no 23 | auth_admin_keep 24 | 25 | 26 | 27 | 28 | Launch PPP daemon 29 | Launching PPP daemon requires privileges 30 | 31 | no 32 | auth_admin_keep 33 | 34 | 35 | 36 | 37 | Set RfKill State 38 | Setting RfKill State requires privileges 39 | 40 | no 41 | auth_admin_keep 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /data/configs/Makefile.am: -------------------------------------------------------------------------------- 1 | dbusdir = $(datadir)/dbus-1/system.d 2 | dbus_DATA = org.blueman.Mechanism.conf 3 | 4 | dbus_servicesdir = $(datadir)/dbus-1/system-services 5 | dbus_services_DATA = org.blueman.Mechanism.service 6 | 7 | dbus_sessdir = $(datadir)/dbus-1/services 8 | dbus_sess_DATA = org.blueman.Applet.service 9 | 10 | dbus_managerdir = $(datadir)/dbus-1/services 11 | dbus_manager_DATA = org.blueman.Manager.service 12 | 13 | if SYSTEMD_SYSTEM_UNIT_DIR 14 | systemd_systemdir = $(systemd_system_unit_dir) 15 | systemd_system_DATA = blueman-mechanism.service 16 | endif 17 | 18 | if SYSTEMD_USER_UNIT_DIR 19 | systemd_userdir = $(systemd_user_unit_dir) 20 | systemd_user_DATA = blueman-applet.service blueman-manager.service 21 | endif 22 | 23 | if HAVE_POLKIT 24 | policykitactionsdir = $(datadir)/polkit-1/actions 25 | policykitactions_DATA = org.blueman.policy 26 | 27 | org.blueman.policy: org.blueman.policy.in 28 | $(AM_V_GEN) GETTEXTDATADIR=$(top_srcdir)/data/configs $(MSGFMT) --xml -d $(top_srcdir)/po/ -o $@ --template $< 29 | 30 | 31 | policykitrulesdir = $(datadir)/polkit-1/rules.d 32 | policykitrules_DATA = blueman.rules 33 | endif 34 | 35 | EXTRA_DIST = \ 36 | its \ 37 | blueman-applet.service.in \ 38 | blueman-manager.service.in \ 39 | blueman-mechanism.service.in \ 40 | org.blueman.Mechanism.conf \ 41 | org.blueman.Applet.service.in \ 42 | org.blueman.Manager.service.in \ 43 | org.blueman.Mechanism.service.in \ 44 | org.blueman.policy.in \ 45 | blueman.rules 46 | 47 | CLEANFILES = \ 48 | blueman-applet.service \ 49 | blueman-manager.service \ 50 | blueman-mechanism.service \ 51 | org.blueman.Manager.service \ 52 | org.blueman.Mechanism.service \ 53 | org.blueman.Applet.service \ 54 | org.blueman.policy \ 55 | $(BUILT_SOURCES) 56 | 57 | DISTCLEANFILES = \ 58 | $(CLEANFILES) 59 | 60 | -------------------------------------------------------------------------------- /blueman/plugins/ServicePlugin.py: -------------------------------------------------------------------------------- 1 | from typing import List, Tuple, Union, TYPE_CHECKING 2 | 3 | 4 | import gi 5 | gi.require_version("Gtk", "3.0") 6 | from gi.repository import Gtk 7 | 8 | if TYPE_CHECKING: 9 | from typing_extensions import Literal 10 | 11 | from blueman.main.Services import BluemanServices 12 | 13 | 14 | class ServicePlugin: 15 | _options: List[str] 16 | instances: List["ServicePlugin"] = [] 17 | __plugin_info__: Tuple[str, str] 18 | 19 | def __init__(self, parent: "BluemanServices"): 20 | ServicePlugin.instances.append(self) 21 | self._options = [] 22 | self.parent = parent 23 | self.widget: Gtk.Grid 24 | 25 | self.__is_exposed = False 26 | 27 | # call when option has changed. 28 | def option_changed_notify(self, option_id: str, state: bool = True) -> None: 29 | 30 | if option_id not in self._options: 31 | self._options.append(option_id) 32 | else: 33 | if state: 34 | self._options.remove(option_id) 35 | 36 | self.parent.option_changed() 37 | 38 | def get_options(self) -> List[str]: 39 | return self._options 40 | 41 | def clear_options(self) -> None: 42 | self._options = [] 43 | 44 | def on_load(self) -> None: 45 | pass 46 | 47 | def on_unload(self) -> None: 48 | pass 49 | 50 | # return true if apply button should be sensitive or false if not. -1 to force disabled 51 | def on_query_apply_state(self) -> Union[bool, "Literal[-1]"]: 52 | return False 53 | 54 | def on_apply(self) -> None: 55 | pass 56 | 57 | # called when current plugin's page is selected. The plugin's widget should be shown 58 | def on_enter(self) -> None: 59 | pass 60 | 61 | # called when current plugin's page is changed to another. The plugin's widget should be hidden. 62 | def on_leave(self) -> None: 63 | pass 64 | -------------------------------------------------------------------------------- /blueman/bluez/obex/Transfer.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import List, Optional 3 | from blueman.bluemantyping import ObjectPath 4 | 5 | from blueman.bluez.obex.Base import Base 6 | from gi.repository import GObject, Gio, GLib 7 | 8 | from blueman.bluemantyping import GSignals 9 | 10 | 11 | class Transfer(Base): 12 | __gsignals__: GSignals = { 13 | 'progress': (GObject.SignalFlags.NO_HOOKS, None, (int,)), 14 | 'completed': (GObject.SignalFlags.NO_HOOKS, None, ()), 15 | 'error': (GObject.SignalFlags.NO_HOOKS, None, ()) 16 | } 17 | 18 | _interface_name = 'org.bluez.obex.Transfer1' 19 | 20 | def __init__(self, obj_path: ObjectPath): 21 | super().__init__(obj_path=obj_path) 22 | 23 | @property 24 | def filename(self) -> Optional[str]: 25 | name: Optional[str] = self.get("Filename") 26 | return name 27 | 28 | @property 29 | def name(self) -> str: 30 | name: str = self.get("Name") 31 | return name 32 | 33 | @property 34 | def session(self) -> ObjectPath: 35 | session: ObjectPath = self.get("Session") 36 | return session 37 | 38 | @property 39 | def size(self) -> Optional[int]: 40 | size: Optional[int] = self.get("Size") 41 | return size 42 | 43 | def _properties_changed(self, _proxy: Gio.DBusProxy, changed_properties: GLib.Variant, 44 | _invalidated_properties: List[str]) -> None: 45 | logging.debug(f"{changed_properties}") 46 | for name, value in changed_properties.unpack().items(): 47 | logging.debug(f"{self.get_object_path()} {name} {value}") 48 | if name == 'Transferred': 49 | self.emit('progress', value) 50 | elif name == 'Status': 51 | if value == 'complete': 52 | self.emit('completed') 53 | elif value == 'error': 54 | self.emit('error') 55 | -------------------------------------------------------------------------------- /blueman/bluez/obex/Client.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from blueman.bluez.errors import BluezDBusException 4 | from blueman.bluez.obex.Base import Base 5 | from gi.repository import GObject, GLib 6 | 7 | from blueman.bluemantyping import GSignals, ObjectPath, BtAddress 8 | 9 | 10 | class Client(Base): 11 | __gsignals__: GSignals = { 12 | 'session-failed': (GObject.SignalFlags.NO_HOOKS, None, (object,)), 13 | } 14 | 15 | _interface_name = 'org.bluez.obex.Client1' 16 | _obj_path: ObjectPath = ObjectPath('/org/bluez/obex') 17 | 18 | def __init__(self) -> None: 19 | super().__init__(obj_path=self._obj_path) 20 | 21 | def create_session(self, dest_addr: BtAddress, source_addr: BtAddress = BtAddress("00:00:00:00:00:00"), 22 | pattern: str = "opp") -> None: 23 | def on_session_created(session_path: ObjectPath) -> None: 24 | logging.info(f"{dest_addr} {source_addr} {pattern} {session_path}") 25 | 26 | def on_session_failed(error: BluezDBusException) -> None: 27 | logging.error(f"{dest_addr} {source_addr} {pattern} {error}") 28 | self.emit("session-failed", error) 29 | 30 | v_source_addr = GLib.Variant('s', source_addr) 31 | v_pattern = GLib.Variant('s', pattern) 32 | param = GLib.Variant('(sa{sv})', (dest_addr, {"Source": v_source_addr, "Target": v_pattern})) 33 | self._call('CreateSession', param, reply_handler=on_session_created, error_handler=on_session_failed) 34 | 35 | def remove_session(self, session_path: ObjectPath) -> None: 36 | def on_session_removed() -> None: 37 | logging.info(session_path) 38 | 39 | def on_session_remove_failed(error: BluezDBusException) -> None: 40 | logging.error(f"{session_path} {error}") 41 | 42 | param = GLib.Variant('(o)', (session_path,)) 43 | self._call('RemoveSession', param, reply_handler=on_session_removed, 44 | error_handler=on_session_remove_failed) 45 | -------------------------------------------------------------------------------- /sendto/blueman_sendto.py.in: -------------------------------------------------------------------------------- 1 | from gi.repository import @FILEMANAGER@, GObject, Gio 2 | 3 | 4 | # noinspection PyMissingConstructor 5 | class BluemanSendtoExtension(GObject.GObject, @FILEMANAGER@.MenuProvider): 6 | def __init__(self): 7 | pass 8 | 9 | def on_menu_activate(self, menu, files): 10 | send_files = [] 11 | 12 | for f in files: 13 | if f.is_directory(): 14 | print("Skipping directory") 15 | continue 16 | elif f.is_gone(): 17 | print("Skipping none existing file") 18 | continue 19 | 20 | gfile = f.get_location() 21 | send_files.append("\"%s\"" % gfile.get_path()) 22 | 23 | if len(send_files) == 0: 24 | return 25 | else: 26 | cmd = "blueman-sendto %s" % " ".join(send_files) 27 | flags = Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION 28 | appinfo = Gio.AppInfo.create_from_commandline(cmd, "blueman-sendto", flags) 29 | print("Running: %s" % cmd) 30 | 31 | launched = appinfo.launch() 32 | if not launched: 33 | print("*** Failed to launch program ***") 34 | 35 | def get_file_items(self, *args): 36 | files = args[-1] 37 | 38 | if len(files) == 0: 39 | return 40 | 41 | # We do not support sending whole directories 42 | for f in files: 43 | if f.is_directory(): 44 | return 45 | 46 | item = @FILEMANAGER@.MenuItem( 47 | name='BluemanSendto::blueman_send_files', 48 | label='Send files over bluetooth', 49 | tip='Sends files over bluetooth with blueman', 50 | icon='blueman' 51 | ) 52 | 53 | item.connect('activate', self.on_menu_activate, files) 54 | 55 | return [item] 56 | 57 | # stub to avoid potential warning (nautilus throws fit) 58 | def get_background_items(self, *args): 59 | return [] 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Blueman is a GTK+ Bluetooth Manager 4 | 5 | Blueman is designed to provide a simple yet effective means for 6 | controlling the BlueZ API and simplifying Bluetooth tasks, such as: 7 | 8 | * Connecting to dial-up networks 9 | * Connecting to / Creating Bluetooth networks 10 | * Connecting to input devices 11 | * Connecting to audio devices 12 | * Sending / Receiving files via OBEX 13 | * Pairing 14 | 15 | It is lightweight, easy to use, Python based, and GPL licensed. 16 | 17 | ## Installing 18 | 19 | See [Dependencies.md](Dependencies.md) for a list of build and runtime dependencies. 20 | 21 | To install a packaged release of blueman, run `./configure && make && make install`. 22 | 23 | To generate and run a configure script from source, run `./autogen.sh`. 24 | 25 | If you are packaging it for your distribution, please make sure to pass `--disable-schemas-compile` and run `glib-compile-schemas /datadir/glib-2.0/schemas` as part of your (un)install phase. 26 | 27 | [![Packaging status](https://repology.org/badge/tiny-repos/blueman.svg?header=blueman%20packages)](https://repology.org/project/blueman/versions) 28 | 29 | ## Support / Troubleshooting 30 | 31 | If you're reporting a bug, please read the [Troubleshooting page](https://github.com/blueman-project/blueman/wiki/Troubleshooting) to provide all relevant info. 32 | 33 | Feel free to [open a GitHub issue](https://github.com/blueman-project/blueman/issues/new) to file bugs, or ask about anything you need help with. 34 | 35 | ## Contributing 36 | 37 | Fork, make your changes, and issue a pull request. If you just want to edit a single file, GitHub will guide you through that process. 38 | 39 | ### Translate 40 | 41 | Translations are managed on Hosted Weblate. 42 | 43 | [![Translation status](https://hosted.weblate.org/widgets/blueman/-/svg-badge.svg)](https://hosted.weblate.org/engage/blueman/) 44 | 45 | ## License 46 | 47 | All parts of the software are licensed under GPLv3 (or GPLv2) and allow redistribution under any later version. 48 | -------------------------------------------------------------------------------- /blueman/plugins/applet/ConnectionNotifier.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from gettext import gettext as _ 3 | from typing import Any, Dict, Union 4 | from blueman.bluemantyping import ObjectPath 5 | 6 | from blueman.bluez.Device import Device 7 | from blueman.gui.Notification import Notification, _NotificationBubble, _NotificationDialog 8 | from blueman.main.BatteryWatcher import BatteryWatcher 9 | from blueman.plugins.AppletPlugin import AppletPlugin 10 | from gi.repository import GLib 11 | 12 | 13 | class ConnectionNotifier(AppletPlugin): 14 | __author__ = "cschramm" 15 | __icon__ = "bluetooth-symbolic" 16 | __description__ = _("Shows desktop notifications when devices get connected or disconnected.") 17 | 18 | _notifications: Dict[ObjectPath, Union[_NotificationBubble, _NotificationDialog]] = {} 19 | 20 | def on_load(self) -> None: 21 | self._battery_watcher = BatteryWatcher(self._on_battery_update) 22 | 23 | def on_unload(self) -> None: 24 | del self._battery_watcher 25 | 26 | def on_device_property_changed(self, path: ObjectPath, key: str, value: Any) -> None: 27 | if key == "Connected": 28 | device = Device(obj_path=path) 29 | if value: 30 | self._notifications[path] = notification = Notification( 31 | device.display_name, 32 | _('Connected'), 33 | icon_name=device["Icon"] 34 | ) 35 | notification.show() 36 | else: 37 | Notification(device.display_name, _('Disconnected'), icon_name=device["Icon"]).show() 38 | 39 | def _on_battery_update(self, path: ObjectPath, value: int) -> None: 40 | notification = self._notifications.pop(path, None) 41 | if notification: 42 | try: 43 | notification.set_message(f"{_('Connected')} {value}%") 44 | notification.set_notification_icon("battery") 45 | except GLib.Error: 46 | logging.error("Failed to update notification", exc_info=True) 47 | -------------------------------------------------------------------------------- /blueman/services/meta/NetworkService.py: -------------------------------------------------------------------------------- 1 | from gettext import gettext as _ 2 | from typing import Optional, Callable, List, Set 3 | 4 | from blueman.main.DBusProxies import AppletService 5 | 6 | from blueman.Service import Service, Action, Instance 7 | from blueman.bluez.Device import Device 8 | from blueman.bluez.Network import Network 9 | from blueman.bluez.errors import BluezDBusException 10 | 11 | 12 | class NetworkService(Service): 13 | def __init__(self, device: Device, uuid: str): 14 | super().__init__(device, uuid) 15 | self._service = Network(obj_path=device.get_object_path()) 16 | 17 | @property 18 | def available(self) -> bool: 19 | # This interface is only available after pairing 20 | paired: bool = self.device["Paired"] 21 | return paired 22 | 23 | @property 24 | def connectable(self) -> bool: 25 | return not self.available or not self._service["Connected"] 26 | 27 | @property 28 | def connected_instances(self) -> List[Instance]: 29 | return [] if self.connectable else [Instance(self.name)] 30 | 31 | def connect( 32 | self, 33 | reply_handler: Optional[Callable[[str], None]] = None, 34 | error_handler: Optional[Callable[[BluezDBusException], None]] = None, 35 | ) -> None: 36 | self._service.connect(self.uuid, reply_handler=reply_handler, error_handler=error_handler) 37 | 38 | def disconnect( 39 | self, 40 | reply_handler: Optional[Callable[[], None]] = None, 41 | error_handler: Optional[Callable[[BluezDBusException], None]] = None, 42 | ) -> None: 43 | self._service.disconnect(reply_handler=reply_handler, error_handler=error_handler) 44 | 45 | @property 46 | def common_actions(self) -> Set[Action]: 47 | def renew() -> None: 48 | AppletService().DhcpClient('(s)', self.device.get_object_path()) 49 | 50 | return {Action( 51 | _("Renew IP Address"), 52 | "view-refresh", 53 | {"DhcpClient"}, 54 | renew 55 | )} 56 | -------------------------------------------------------------------------------- /data/icons/hicolor/scalable/actions/blueman-pair-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 29 | 49 | 52 | 53 | 58 | 59 | -------------------------------------------------------------------------------- /apps/blueman-mechanism.in: -------------------------------------------------------------------------------- 1 | #!@PYTHON@ 2 | import sys 3 | import os 4 | import logging 5 | 6 | from blueman.main.MechanismApplication import MechanismApplication 7 | 8 | # support running uninstalled 9 | _dirname = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) 10 | if 'BLUEMAN_SOURCE' in os.environ: 11 | sys.path = [_dirname, os.path.join(_dirname, 'module', '.libs')] + sys.path 12 | os.environ["GSETTINGS_SCHEMA_DIR"] = os.path.join(_dirname, "data") 13 | 14 | from blueman.Functions import set_proc_title, create_logger, create_parser 15 | 16 | 17 | class StreamToLogger: 18 | """ 19 | Fake file-like stream object that redirects writes to a logger instance. 20 | """ 21 | def __init__(self, logger, log_level=logging.INFO): 22 | self.logger = logger 23 | self.log_level = log_level 24 | self.linebuf = '' 25 | 26 | def write(self, buf): 27 | for line in buf.rstrip().splitlines(): 28 | self.logger.log(self.log_level, line.rstrip()) 29 | 30 | def flush(self): 31 | pass 32 | 33 | 34 | parser = create_parser(syslog=False) 35 | parser.add_argument("-d", "--debug", dest="debug", action="store_true") 36 | parser.add_argument("-s", "--stop-timer", dest="stoptimer", action="store_true") 37 | args = parser.parse_args() 38 | 39 | if args.LEVEL.upper() == "DEBUG": 40 | log_level = logging.DEBUG 41 | elif args.LEVEL.upper() == "INFO": 42 | log_level = logging.INFO 43 | elif args.LEVEL.upper() == "WARNING": 44 | log_level = logging.WARNING 45 | elif args.LEVEL.upper() == "ERROR": 46 | log_level = logging.ERROR 47 | elif args.LEVEL.upper() == "CRITICAL": 48 | log_level = logging.CRITICAL 49 | else: 50 | log_level = logging.WARNING 51 | 52 | logger = create_logger(log_level, "blueman-mechanism", syslog=True) 53 | 54 | if args.debug: 55 | logging.info("Enabled verbose output") 56 | 57 | sl = StreamToLogger(logger, logging.INFO) 58 | sys.stdout = sl 59 | 60 | sl = StreamToLogger(logger, logging.ERROR) 61 | sys.stderr = sl 62 | 63 | logging.info("Starting blueman-mechanism") 64 | 65 | os.environ["PATH"] = "/usr/bin:/bin:/usr/sbin:/sbin" 66 | 67 | set_proc_title() 68 | app = MechanismApplication(args.stoptimer) 69 | app.run() 70 | -------------------------------------------------------------------------------- /test/testhelpers/DBusMock.py: -------------------------------------------------------------------------------- 1 | from dbusmock import DBusTestCase 2 | from gi.repository import Gio, GLib 3 | 4 | 5 | class DBusMockObject: 6 | def __init__(self, name: str, path: str, system_bus: bool) -> None: 7 | self._proxy = Gio.DBusProxy( 8 | g_name=name, 9 | g_interface_name="org.freedesktop.DBus.Mock", 10 | g_object_path=path, 11 | g_connection=Gio.bus_get_sync(Gio.BusType.SYSTEM if system_bus else Gio.BusType.SESSION), 12 | ) 13 | self._proxy.init() 14 | 15 | def add_method(self, interface: str, name: str, in_sig: str, out_sig: str, script: str) -> None: 16 | self._proxy.call_sync("AddMethod", GLib.Variant("(sssss)", (interface, name, in_sig, out_sig, script)), 17 | Gio.DBusCallFlags.NONE, GLib.MAXINT) 18 | 19 | def add_property(self, interface: str, name: str, value: GLib.Variant) -> None: 20 | self._proxy.call_sync("AddProperty", GLib.Variant("(ssv)", (interface, name, value)), 21 | Gio.DBusCallFlags.NONE, GLib.MAXINT) 22 | 23 | def set_property(self, interface: str, name: str, value: GLib.Variant) -> None: 24 | self._proxy.call_sync("org.freedesktop.DBus.Properties.Set", GLib.Variant("(ssv)", (interface, name, value)), 25 | Gio.DBusCallFlags.NONE, GLib.MAXINT) 26 | 27 | 28 | class DBusMock(DBusMockObject): 29 | def __init__(self, name: str, path: str, system_bus: bool) -> None: 30 | self._system_bus = system_bus 31 | self._process = DBusTestCase.spawn_server(name, path, "org.freedesktop.DBus.Mock", system_bus=system_bus) 32 | super().__init__(name, path, system_bus) 33 | 34 | def __enter__(self): 35 | return self 36 | 37 | def __exit__(self, exc_type, exc_val, exc_tb): 38 | self._process.terminate() 39 | self._process.wait() 40 | 41 | def add_object(self, path: str) -> DBusMockObject: 42 | self._proxy.call_sync( 43 | "AddObject", GLib.Variant("(ssa{sv}a(ssss))", (path, "org.freedesktop.DBus.Mock", {}, [])), 44 | Gio.DBusCallFlags.NONE, GLib.MAXINT) 45 | return DBusMockObject(self._proxy.props.g_name, path, self._system_bus) 46 | -------------------------------------------------------------------------------- /blueman/Service.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import Optional, Callable, List, Set, Collection 3 | 4 | from blueman.Sdp import ServiceUUID 5 | from blueman.bluez.Device import Device 6 | 7 | 8 | class Instance: 9 | def __init__(self, name: str, port: int = 0) -> None: 10 | self.name = name 11 | self.port = port 12 | 13 | 14 | class Action: 15 | def __init__(self, title: str, icon: str, plugins: Collection[str], callback: Callable[[], None]) -> None: 16 | self.title = title 17 | self.icon = icon 18 | self.plugins = plugins 19 | self.callback = callback 20 | 21 | def __eq__(self, other: object) -> bool: 22 | return isinstance(other, Action) and self.title == other.title 23 | 24 | def __hash__(self) -> int: 25 | return hash(self.title) 26 | 27 | 28 | class Service(ABC): 29 | __svclass_id__: int 30 | __description__ = None 31 | __icon__: str 32 | __priority__: int 33 | 34 | def __init__(self, device: Device, uuid: str): 35 | self.__device = device 36 | self.__uuid = uuid 37 | 38 | @property 39 | def name(self) -> str: 40 | return ServiceUUID(self.__uuid).name 41 | 42 | @property 43 | def device(self) -> Device: 44 | return self.__device 45 | 46 | @property 47 | def uuid(self) -> str: 48 | return self.__uuid 49 | 50 | @property 51 | def short_uuid(self) -> Optional[int]: 52 | return ServiceUUID(self.__uuid).short_uuid 53 | 54 | @property 55 | def description(self) -> Optional[str]: 56 | return self.__description__ 57 | 58 | @property 59 | def icon(self) -> str: 60 | return self.__icon__ 61 | 62 | @property 63 | def priority(self) -> int: 64 | return self.__priority__ 65 | 66 | @property 67 | @abstractmethod 68 | def available(self) -> bool: 69 | ... 70 | 71 | @property 72 | @abstractmethod 73 | def connectable(self) -> bool: 74 | ... 75 | 76 | @property 77 | @abstractmethod 78 | def connected_instances(self) -> List[Instance]: 79 | ... 80 | 81 | @property 82 | def common_actions(self) -> Set[Action]: 83 | return set() 84 | -------------------------------------------------------------------------------- /Dependencies.md: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | 3 | ## Runtime dependencies 4 | 5 | * [BlueZ 5](http://www.bluez.org/) (>= 5.48) 6 | * [dbus](http://www.freedesktop.org/wiki/Software/dbus/) (>= 1.9.18) 7 | * [GdkPixbuf](http://www.gtk.org/) (GI bindings) 8 | * [GLib 2, Gio 2](http://www.gtk.org/) (>= 2.32) (GI bindings) 9 | * [GTK+ 3](http://www.gtk.org/) (>= 3.24) (GI bindings) 10 | * [gnome-icon-theme](https://git.gnome.org/browse/adwaita-icon-theme/), [mate-icon-theme](https://github.com/mate-desktop/mate-icon-theme), [adwaita-icon-theme](https://github.com/GNOME/adwaita-icon-theme), [elementary-xfce](https://github.com/shimmerproject/elementary-xfce), or [Papirus](https://github.com/PapirusDevelopmentTeam/papirus-icon-theme) 11 | * [notification-daemon](https://specifications.freedesktop.org/notification-spec/notification-spec-latest.html) that provides dbus name org.freedesktop.Notifications 12 | * [obexd 5](http://www.bluez.org/) 13 | * [Pango](http://www.gtk.org/) (GI bindings) 14 | * [pulseaudio](http://www.freedesktop.org/wiki/Software/PulseAudio/) (optional; its bluetooth module is required to actually use audio devices) 15 | * [pycairo](http://cairographics.org/pycairo/) 16 | * [PyGObject 3](https://wiki.gnome.org/PyGObject) (>= 3.27.2) 17 | * [Python](http://www.python.org/) (>= 3.8) 18 | * [net-tools](http://net-tools.sourceforge.net/) for blueman 2.0 and net-tools __or__ [iproute2](https://wiki.linuxfoundation.org/networking/iproute2) for blueman 2.1 19 | * [libnm](https://wiki.gnome.org/Projects/NetworkManager) For managing DUN and PANU connection (GI bindings) 20 | 21 | ## Additional test dependencies 22 | 23 | * [python-dbusmock](https://github.com/martinpitt/python-dbusmock) 24 | * [dbus-python](http://www.freedesktop.org/wiki/Software/DBusBindings/#python) 25 | 26 | ## Build dependencies 27 | 28 | * [BlueZ 5](http://www.bluez.org/) 29 | * [Cython](http://www.cython.org/) 30 | * [GLib 2](http://www.gtk.org/) (>= 2.32) 31 | * [gettext](https://www.gnu.org/software/gettext/) 32 | * [PyGObject 3](https://wiki.gnome.org/PyGObject) (>= 3.27.2) 33 | * [Python](http://www.python.org/) (>= 3.8) 34 | 35 | ## Additional dependencies for VCS version 36 | 37 | * [automake](https://www.gnu.org/software/automake/) 38 | * [autoconf](https://www.gnu.org/software/autoconf/) 39 | * [libtool](http://www.gnu.org/software/libtool/) 40 | -------------------------------------------------------------------------------- /data/Makefile.am: -------------------------------------------------------------------------------- 1 | autostartdir = $(sysconfdir)/xdg/autostart 2 | autostart_in_files = blueman.desktop.in 3 | autostart_DATA = $(autostart_in_files:.desktop.in=.desktop) 4 | 5 | $(autostart_DATA): $(autostart_in_files) 6 | $(AM_V_GEN) $(MSGFMT) --desktop --template $< -d $(top_srcdir)/po -o $@ 7 | 8 | appdir = $(datadir)/applications 9 | app_in_files = blueman-manager.desktop.in 10 | app_DATA = $(app_in_files:.desktop.in=.desktop) 11 | 12 | $(app_DATA): $(app_in_files) 13 | $(AM_V_GEN) $(MSGFMT) --desktop --template $< -d $(top_srcdir)/po -o $@ 14 | 15 | if HAVE_THUNAR 16 | thunardir = $(datadir)/Thunar/sendto 17 | thunar_in_files = thunar-sendto-blueman.desktop.in 18 | thunar_DATA = $(thunar_in_files:.desktop.in=.desktop) 19 | 20 | $(thunar_DATA): $(thunar_in_files) 21 | $(AM_V_GEN) $(MSGFMT) --desktop --template $< -d $(top_srcdir)/po -o $@ 22 | endif 23 | 24 | if HAVE_SETTINGS 25 | settingsdir = $(datadir)/applications 26 | settings_in_files = blueman-adapters.desktop.in 27 | settings_DATA = $(settings_in_files:.desktop.in=.desktop) 28 | 29 | $(settings_DATA): $(settings_in_files) 30 | $(AM_V_GEN) $(MSGFMT) --desktop --template $< -d $(top_srcdir)/po -o $@ 31 | endif 32 | 33 | gsettings_SCHEMAS = org.blueman.gschema.xml 34 | 35 | # include the appropriate makefile rules for schema handling 36 | @GSETTINGS_RULES@ 37 | 38 | # GTK icon cache 39 | gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor 40 | 41 | install-data-hook: update-icon-cache 42 | uninstall-hook: update-icon-cache 43 | 44 | update-icon-cache: 45 | if test -z "$(DESTDIR)"; then \ 46 | echo "Updating Gtk icon cache."; \ 47 | $(gtk_update_icon_cache); \ 48 | else \ 49 | echo "*** Icon cache not updated. After (un)install, run this:"; \ 50 | echo "*** $(gtk_update_icon_cache)"; \ 51 | fi 52 | 53 | EXTRA_DIST = $(autostart_in_files) \ 54 | $(app_in_files) \ 55 | $(thunar_in_files) \ 56 | $(settings_in_files) \ 57 | $(gsettings_SCHEMAS) \ 58 | meson.build 59 | 60 | SUBDIRS = \ 61 | configs \ 62 | icons \ 63 | ui \ 64 | man 65 | 66 | CLEANFILES = \ 67 | thunar-sendto-blueman.desktop \ 68 | blueman-adapters.desktop \ 69 | blueman-manager.desktop \ 70 | blueman.desktop \ 71 | $(BUILT_SOURCES) \ 72 | *.gschema.valid \ 73 | gschemas.compiled 74 | 75 | DISTCLEANFILES = \ 76 | $(CLEANFILES) 77 | -------------------------------------------------------------------------------- /blueman/plugins/mechanism/Network.py: -------------------------------------------------------------------------------- 1 | from typing import Callable, Union, Dict, Type, TYPE_CHECKING 2 | from blueman.bluemantyping import ObjectPath 3 | 4 | from blueman.bluez.Network import Network as BluezNetwork 5 | from blueman.plugins.MechanismPlugin import MechanismPlugin 6 | from blueman.main.NetConf import NetConf, DnsMasqHandler, DhcpdHandler, UdhcpdHandler 7 | 8 | if TYPE_CHECKING: 9 | from blueman.main.NetConf import DHCPHandler 10 | 11 | DHCPDHANDLERS: Dict[str, Type["DHCPHandler"]] = { 12 | "DnsMasqHandler": DnsMasqHandler, 13 | "DhcpdHandler": DhcpdHandler, 14 | "UdhcpdHandler": UdhcpdHandler 15 | } 16 | 17 | 18 | class Network(MechanismPlugin): 19 | def on_load(self) -> None: 20 | self.parent.add_method("DhcpClient", ("s",), "s", self._run_dhcp_client, pass_sender=True, is_async=True) 21 | self.parent.add_method("EnableNetwork", ("s", "s", "s", "b"), "", self._enable_network, pass_sender=True) 22 | self.parent.add_method("DisableNetwork", (), "", self._disable_network, pass_sender=True) 23 | 24 | def _run_dhcp_client(self, object_path: ObjectPath, caller: str, ok: Callable[[str], None], 25 | err: Callable[[Union[Exception, int]], None]) -> None: 26 | self.timer.stop() 27 | 28 | self.confirm_authorization(caller, "org.blueman.dhcp.client") 29 | 30 | from blueman.main.DhcpClient import DhcpClient 31 | 32 | def dh_error(_dh: DhcpClient, num: int) -> None: 33 | err(num) 34 | self.timer.resume() 35 | 36 | def dh_connected(_dh: DhcpClient, ip: str) -> None: 37 | ok(ip) 38 | self.timer.resume() 39 | 40 | dh = DhcpClient(BluezNetwork(obj_path=object_path)["Interface"]) 41 | dh.connect("error-occurred", dh_error) 42 | dh.connect("connected", dh_connected) 43 | try: 44 | dh.run() 45 | except Exception as e: 46 | err(e) 47 | 48 | def _enable_network(self, ip_address: str, netmask: str, dhcp_handler: str, address_changed: bool, 49 | caller: str) -> None: 50 | self.confirm_authorization(caller, "org.blueman.network.setup") 51 | NetConf.apply_settings(ip_address, netmask, DHCPDHANDLERS[dhcp_handler], address_changed) 52 | 53 | def _disable_network(self, caller: str) -> None: 54 | self.confirm_authorization(caller, "org.blueman.network.setup") 55 | NetConf.clean_up() 56 | -------------------------------------------------------------------------------- /blueman/gui/DeviceSelectorDialog.py: -------------------------------------------------------------------------------- 1 | from gettext import gettext as _ 2 | from typing import Optional, Tuple 3 | 4 | from blueman.bluez.Device import Device 5 | from blueman.gui.DeviceList import DeviceList 6 | from blueman.gui.DeviceSelectorWidget import DeviceSelectorWidget 7 | 8 | import gi 9 | gi.require_version("Gtk", "3.0") 10 | from gi.repository import Gtk 11 | 12 | 13 | class DeviceSelectorDialog(Gtk.Dialog): 14 | selection: Optional[Tuple[str, Optional[Device]]] 15 | 16 | def __init__(self, title: str = _("Select Device"), parent: Optional[Gtk.Container] = None, discover: bool = True, 17 | adapter_name: Optional[str] = None) -> None: 18 | super().__init__(title=title, name="DeviceSelectorDialog", parent=parent, icon_name="blueman", resizable=False) 19 | self.add_buttons(_("_Cancel"), Gtk.ResponseType.REJECT, _("_OK"), Gtk.ResponseType.ACCEPT) 20 | 21 | self.vbox.props.halign = Gtk.Align.CENTER 22 | self.vbox.props.valign = Gtk.Align.CENTER 23 | self.vbox.props.hexpand = True 24 | self.vbox.props.vexpand = True 25 | self.vbox.props.margin = 6 26 | 27 | self.selector = DeviceSelectorWidget(adapter_name=adapter_name, visible=True) 28 | self.vbox.pack_start(self.selector, True, True, 0) 29 | 30 | selected_device = self.selector.List.get_selected_device() 31 | if selected_device is not None: 32 | self.selection = selected_device["Adapter"], selected_device 33 | else: 34 | self.selection = None 35 | 36 | self.selector.List.connect("device-selected", self.on_device_selected) 37 | self.selector.List.connect("adapter-changed", self.on_adapter_changed) 38 | if discover: 39 | self.selector.List.discover_devices() 40 | 41 | self.selector.List.connect("row-activated", self.on_row_activated) 42 | 43 | def close(self) -> None: 44 | self.selector.destroy() 45 | super().close() 46 | 47 | def on_row_activated(self, _treeview: Gtk.TreeView, _path: Gtk.TreePath, _view_column: Gtk.TreeViewColumn, 48 | *_args: object) -> None: 49 | self.response(Gtk.ResponseType.ACCEPT) 50 | 51 | def on_adapter_changed(self, _devlist: DeviceList, _adapter: str) -> None: 52 | self.selection = None 53 | 54 | def on_device_selected(self, devlist: DeviceList, device: Optional[Device], _tree_iter: Gtk.TreeIter) -> None: 55 | assert devlist.Adapter is not None 56 | self.selection = (devlist.Adapter.get_object_path(), device) 57 | -------------------------------------------------------------------------------- /blueman/plugins/manager/Notes.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from gettext import gettext as _ 3 | from tempfile import NamedTemporaryFile 4 | from typing import List 5 | 6 | from blueman.Functions import create_menuitem, launch 7 | from blueman.bluez.Device import Device 8 | from blueman.gui.manager.ManagerDeviceMenu import MenuItemsProvider, ManagerDeviceMenu, DeviceMenuItem 9 | from blueman.main.Builder import Builder 10 | from blueman.plugins.ManagerPlugin import ManagerPlugin 11 | 12 | import gi 13 | gi.require_version("Gtk", "3.0") 14 | from gi.repository import Gtk 15 | 16 | 17 | def send_note_cb(dialog: Gtk.Dialog, response_id: int, device_address: str, text_view: Gtk.Entry) -> None: 18 | text = text_view.get_buffer().props.text 19 | dialog.destroy() 20 | if response_id == Gtk.ResponseType.REJECT: 21 | return 22 | 23 | date = datetime.datetime.now().strftime('%Y%m%dT%H%M00') 24 | data = ('BEGIN:VNOTE \n' 25 | 'VERSION:1.1 \n' 26 | f'BODY;CHARSET=UTF-8: {" ".join(text.splitlines())} \n' 27 | f'DCREATED:{date} \n' 28 | f'LAST-MODIFIED:{date} \n' 29 | 'CLASS:PUBLIC \n' 30 | 'X-IRMC-LUID:000001000000 \n' 31 | 'END:VNOTE \n') 32 | 33 | tempfile = NamedTemporaryFile(suffix='.vnt', prefix='note', delete=False) 34 | tempfile.write(data.encode('utf-8')) 35 | tempfile.close() 36 | launch(f"blueman-sendto --delete --device={device_address}", paths=[tempfile.name]) 37 | 38 | 39 | def send_note(device: Device, parent: Gtk.ApplicationWindow) -> None: 40 | builder = Builder("note.ui") 41 | dialog = builder.get_widget("dialog", Gtk.Dialog) 42 | dialog.set_transient_for(parent) 43 | dialog.props.icon_name = 'blueman' 44 | note = builder.get_widget("note", Gtk.Entry) 45 | dialog.connect('response', send_note_cb, device['Address'], note) 46 | dialog.present() 47 | 48 | 49 | class Notes(ManagerPlugin, MenuItemsProvider): 50 | def on_request_menu_items( 51 | self, 52 | manager_menu: ManagerDeviceMenu, 53 | device: Device, 54 | powered: bool, 55 | ) -> List[DeviceMenuItem]: 56 | if not powered: 57 | return [] 58 | 59 | item = create_menuitem(_("Send _note"), "dialog-information-symbolic") 60 | item.props.tooltip_text = _("Send a text note") 61 | assert isinstance(manager_menu.Blueman.window, Gtk.ApplicationWindow) 62 | window = manager_menu.Blueman.window # https://github.com/python/mypy/issues/2608 63 | item.connect('activate', lambda x: send_note(device, window)) 64 | return [DeviceMenuItem(item, DeviceMenuItem.Group.ACTIONS, 500)] 65 | -------------------------------------------------------------------------------- /data/ui/services-window.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | True 7 | False 8 | 10 9 | Local Services 10 | center 11 | blueman 12 | 13 | 14 | 15 | True 16 | False 17 | 18 | 19 | True 20 | True 21 | True 22 | True 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 0 44 | 0 45 | 46 | 47 | 48 | 49 | _Apply 50 | 80 51 | True 52 | False 53 | True 54 | True 55 | end 56 | 6 57 | True 58 | 59 | 60 | 0 61 | 1 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /blueman/plugins/applet/DhcpClient.py: -------------------------------------------------------------------------------- 1 | from gettext import gettext as _ 2 | import logging 3 | from typing import List, Any 4 | from blueman.bluemantyping import ObjectPath 5 | 6 | from gi.repository import GLib 7 | 8 | from blueman.bluez.Network import AnyNetwork, Network 9 | from blueman.gui.Notification import Notification 10 | from blueman.plugins.AppletPlugin import AppletPlugin 11 | from blueman.main.DBusProxies import Mechanism 12 | 13 | 14 | class DhcpClient(AppletPlugin): 15 | __description__ = _("Provides a basic dhcp client for Bluetooth PAN connections.") 16 | __icon__ = "network-workgroup-symbolic" 17 | __author__ = "Walmis" 18 | 19 | _any_network = None 20 | 21 | def on_load(self) -> None: 22 | self._any_network = AnyNetwork() 23 | self._any_network.connect_signal('property-changed', self._on_network_prop_changed) 24 | 25 | self.querying: List[str] = [] 26 | 27 | self._add_dbus_method("DhcpClient", ("s",), "", self.dhcp_acquire) 28 | 29 | def on_unload(self) -> None: 30 | del self._any_network 31 | 32 | def _on_network_prop_changed(self, _network: AnyNetwork, key: str, value: Any, object_path: ObjectPath) -> None: 33 | if key == "Interface": 34 | if value != "": 35 | self.dhcp_acquire(object_path) 36 | 37 | def dhcp_acquire(self, object_path: ObjectPath) -> None: 38 | device = Network(obj_path=object_path)["Interface"] 39 | 40 | if device not in self.querying: 41 | self.querying.append(device) 42 | else: 43 | return 44 | 45 | if device != "": 46 | def reply(_obj: Mechanism, result: str, _user_data: None) -> None: 47 | logging.info(result) 48 | Notification(_("Bluetooth Network"), 49 | _("Interface %(0)s bound to IP address %(1)s") % {"0": device, "1": result}, 50 | icon_name="network-workgroup").show() 51 | 52 | self.querying.remove(device) 53 | 54 | def err(_obj: Mechanism, result: GLib.Error, _user_data: None) -> None: 55 | logging.warning(result) 56 | Notification(_("Bluetooth Network"), _("Failed to obtain an IP address on %s") % device, 57 | icon_name="network-workgroup").show() 58 | 59 | self.querying.remove(device) 60 | 61 | Notification(_("Bluetooth Network"), _("Trying to obtain an IP address on %s\nPlease wait…" % device), 62 | icon_name="network-workgroup").show() 63 | 64 | m = Mechanism() 65 | m.DhcpClient('(s)', object_path, result_handler=reply, error_handler=err, timeout=120 * 1000) 66 | -------------------------------------------------------------------------------- /data/icons/hicolor/scalable/status/blueman-active-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 30 | 50 | 54 | 60 | 61 | -------------------------------------------------------------------------------- /blueman/gui/DeviceSelectorList.py: -------------------------------------------------------------------------------- 1 | from html import escape 2 | from typing import Optional, Any, List 3 | 4 | from blueman.bluez.Device import Device 5 | from blueman.gui.DeviceList import DeviceList 6 | from gi.repository import Pango 7 | 8 | import gi 9 | 10 | from blueman.gui.GenericList import ListDataDict 11 | 12 | gi.require_version("Gtk", "3.0") 13 | from gi.repository import Gtk 14 | 15 | 16 | class DeviceSelectorList(DeviceList): 17 | def __init__(self, adapter_name: Optional[str] = None) -> None: 18 | tabledata: List[ListDataDict] = [ 19 | # device picture 20 | {"id": "device_icon", "type": str, "renderer": Gtk.CellRendererPixbuf(stock_size=Gtk.IconSize.MENU), 21 | "render_attrs": {"icon_name": 0}}, 22 | # device caption 23 | {"id": "caption", "type": str, "renderer": Gtk.CellRendererText(ellipsize=Pango.EllipsizeMode.END), 24 | "render_attrs": {"markup": 1}, 25 | "view_props": {"expand": True}}, 26 | {"id": "paired_icon", "type": str, "renderer": Gtk.CellRendererPixbuf(stock_size=Gtk.IconSize.MENU), 27 | "render_attrs": {"icon_name": 2}}, 28 | {"id": "trusted_icon", "type": str, "renderer": Gtk.CellRendererPixbuf(stock_size=Gtk.IconSize.MENU), 29 | "render_attrs": {"icon_name": 3}} 30 | ] 31 | 32 | super().__init__(adapter_name, tabledata, headers_visible=False) 33 | 34 | def on_icon_theme_changed(self, _icon_them: Gtk.IconTheme) -> None: 35 | for row in self.liststore: 36 | device = self.get(row.iter, "device")["device"] 37 | self.row_setup_event(row.iter, device) 38 | 39 | def row_setup_event(self, tree_iter: Gtk.TreeIter, device: Device) -> None: 40 | self.row_update_event(tree_iter, "Trusted", device['Trusted']) 41 | self.row_update_event(tree_iter, "Paired", device['Paired']) 42 | self.row_update_event(tree_iter, "Alias", device.display_name) 43 | self.row_update_event(tree_iter, "Icon", device['Icon']) 44 | 45 | def row_update_event(self, tree_iter: Gtk.TreeIter, key: str, value: Any) -> None: 46 | if key == "Trusted": 47 | if value: 48 | self.set(tree_iter, trusted_icon="blueman-trust-symbolic") 49 | else: 50 | self.set(tree_iter, trusted_icon=None) 51 | 52 | elif key == "Paired": 53 | if value: 54 | self.set(tree_iter, paired_icon="blueman-pair-symbolic") 55 | else: 56 | self.set(tree_iter, paired_icon=None) 57 | 58 | elif key == "Alias": 59 | self.set(tree_iter, caption=escape(value)) 60 | 61 | elif key == "Icon": 62 | self.set(tree_iter, device_icon=value) 63 | -------------------------------------------------------------------------------- /blueman/plugins/services/Transfer.py: -------------------------------------------------------------------------------- 1 | from gettext import gettext as _ 2 | import logging 3 | 4 | from blueman.main.Builder import Builder 5 | from blueman.plugins.ServicePlugin import ServicePlugin 6 | from blueman.main.DBusProxies import AppletService 7 | 8 | import gi 9 | gi.require_version("Gtk", "3.0") 10 | from gi.repository import Gtk, Gio 11 | 12 | 13 | class Transfer(ServicePlugin): 14 | __plugin_info__ = (_("Transfer"), "folder") 15 | 16 | def on_load(self) -> None: 17 | 18 | self._builder = Builder("services-transfer.ui") 19 | self.widget = self._builder.get_widget("transfer", Gtk.Grid) 20 | 21 | a = AppletService() 22 | if "TransferService" in a.QueryPlugins(): 23 | self._setup_transfer() 24 | else: 25 | self.widget.props.sensitive = False 26 | self.widget.props.tooltip_text = _("Applet's transfer service plugin is disabled") 27 | 28 | def on_property_changed(self, config: Gio.Settings, key: str) -> None: 29 | value = config[key] 30 | 31 | if key == "shared-path": 32 | self._builder.get_widget(key, Gtk.FileChooserButton).set_current_folder(value) 33 | self.option_changed_notify(key, False) 34 | 35 | def on_apply(self) -> None: 36 | if self.on_query_apply_state(): 37 | for opt in self.get_options(): 38 | if opt == "shared-path": 39 | shared_path = self._builder.get_widget("shared-path", Gtk.FileChooserButton) 40 | self._config["shared-path"] = shared_path.get_filename() 41 | elif opt == "opp-accept": 42 | opp_accept = self._builder.get_widget("opp-accept", Gtk.CheckButton) 43 | self._config["opp-accept"] = opp_accept.get_active() 44 | else: 45 | raise NotImplementedError(f"Unknow option: {opt}") 46 | 47 | self.clear_options() 48 | logging.info("transfer apply") 49 | 50 | def on_query_apply_state(self) -> bool: 51 | opts = self.get_options() 52 | if not opts: 53 | return False 54 | else: 55 | return True 56 | 57 | def _setup_transfer(self) -> None: 58 | self._config = Gio.Settings(schema_id="org.blueman.transfer") 59 | self._config.connect("changed", self.on_property_changed) 60 | 61 | opp_accept = self._builder.get_widget("opp-accept", Gtk.CheckButton) 62 | shared_path = self._builder.get_widget("shared-path", Gtk.FileChooserButton) 63 | 64 | opp_accept.props.active = self._config["opp-accept"] 65 | if self._config["shared-path"]: 66 | shared_path.set_current_folder(self._config["shared-path"]) 67 | 68 | opp_accept.connect("toggled", lambda x: self.option_changed_notify("opp-accept")) 69 | shared_path.connect("file-set", lambda x: self.option_changed_notify("shared-path")) 70 | -------------------------------------------------------------------------------- /blueman/plugins/applet/GameControllerWakelock.py: -------------------------------------------------------------------------------- 1 | from gettext import gettext as _ 2 | import logging 3 | from typing import Any 4 | from blueman.bluemantyping import ObjectPath 5 | 6 | from blueman.bluez.Device import Device 7 | from blueman.Functions import launch 8 | from blueman.plugins.AppletPlugin import AppletPlugin 9 | from blueman.plugins.errors import UnsupportedPlatformError 10 | 11 | import gi 12 | gi.require_version('Gdk', '3.0') 13 | try: 14 | gi.require_version('GdkX11', '3.0') 15 | except ValueError: 16 | raise ImportError("Couldn't find required namespace GdkX11") 17 | 18 | from gi.repository import Gdk 19 | from gi.repository import GdkX11 20 | 21 | 22 | if not isinstance(Gdk.Screen.get_default(), GdkX11.X11Screen): 23 | raise UnsupportedPlatformError('Only X11 platform is supported') 24 | 25 | 26 | class GameControllerWakelock(AppletPlugin): 27 | __description__ = _("Temporarily suspends the screensaver when a bluetooth game controller is connected.") 28 | __author__ = "bwRavencl" 29 | __icon__ = "input-gaming-symbolic" 30 | 31 | def on_load(self) -> None: 32 | self.wake_lock = 0 33 | screen = Gdk.Screen.get_default() 34 | assert screen is not None 35 | window = screen.get_root_window() 36 | assert isinstance(window, GdkX11.X11Window) 37 | self.root_window_id = f"{window.get_xid():#x}" 38 | 39 | def on_unload(self) -> None: 40 | if self.wake_lock: 41 | self.wake_lock = 1 42 | self.xdg_screensaver("resume") 43 | 44 | def on_device_property_changed(self, path: ObjectPath, key: str, value: Any) -> None: 45 | if key == "Connected": 46 | klass = Device(obj_path=path)["Class"] & 0x1fff 47 | 48 | if klass == 0x504 or klass == 0x508: 49 | if value: 50 | self.xdg_screensaver("suspend") 51 | else: 52 | self.xdg_screensaver("resume") 53 | 54 | def xdg_screensaver(self, action: str) -> None: 55 | command = f"xdg-screensaver {action} {self.root_window_id}" 56 | 57 | if action == "resume": 58 | if self.wake_lock <= 0: 59 | self.wake_lock = 0 60 | elif self.wake_lock > 1: 61 | self.wake_lock -= 1 62 | else: 63 | ret = launch(command, sn=False) 64 | if ret: 65 | self.wake_lock -= 1 66 | else: 67 | logging.error(f"{action} failed") 68 | 69 | elif action == "suspend": 70 | if self.wake_lock >= 1: 71 | self.wake_lock += 1 72 | else: 73 | ret = launch(command, sn=False) 74 | if ret: 75 | self.wake_lock += 1 76 | else: 77 | logging.error(f"{action} failed") 78 | 79 | logging.info(f"Number of locks: {self.wake_lock}") 80 | -------------------------------------------------------------------------------- /blueman/main/DhcpClient.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from gi.repository import GObject 4 | from gi.repository import GLib 5 | import socket 6 | import subprocess 7 | import logging 8 | from blueman.Functions import have, get_local_interfaces 9 | from blueman.bluemantyping import GSignals 10 | 11 | 12 | class DhcpClient(GObject.GObject): 13 | __gsignals__: GSignals = { 14 | # arg: interface name eg. ppp0 15 | 'connected': (GObject.SignalFlags.NO_HOOKS, None, (str,)), 16 | 'error-occurred': (GObject.SignalFlags.NO_HOOKS, None, (int,)), 17 | } 18 | 19 | COMMANDS = [ 20 | ["dhclient", "-e", "IF_METRIC=100", "-1"], 21 | ["dhcpcd", "-m", "100"], 22 | ["udhcpc", "-t", "20", "-x", "hostname", socket.gethostname(), "-n", "-i"] 23 | ] 24 | 25 | querying: List[str] = [] 26 | 27 | def __init__(self, interface: str, timeout: int = 30) -> None: 28 | """The interface name has to be trusted / sanitized!""" 29 | super().__init__() 30 | 31 | self._interface = interface 32 | self._timeout = timeout 33 | 34 | self._command = None 35 | for command in self.COMMANDS: 36 | path = have(command[0]) 37 | if path: 38 | self._command = [path] + command[1:] + [self._interface] 39 | break 40 | 41 | def run(self) -> None: 42 | if not self._command: 43 | raise Exception("No DHCP client found, please install dhclient, dhcpcd, or udhcpc") 44 | 45 | if self._interface in DhcpClient.querying: 46 | raise Exception("DHCP already running on this interface") 47 | else: 48 | DhcpClient.querying.append(self._interface) 49 | 50 | self._client = subprocess.Popen(self._command) 51 | GLib.timeout_add(1000, self._check_client) 52 | GLib.timeout_add(self._timeout * 1000, self._on_timeout) 53 | 54 | def _on_timeout(self) -> bool: 55 | if not self._client.poll(): 56 | logging.warning("Timeout reached, terminating DHCP client") 57 | self._client.terminate() 58 | return False 59 | 60 | def _check_client(self) -> bool: 61 | netifs = get_local_interfaces() 62 | status = self._client.poll() 63 | if status == 0: 64 | def complete() -> bool: 65 | ip = netifs[self._interface][0] 66 | logging.info(f"bound to {ip}") 67 | self.emit("connected", ip) 68 | return False 69 | 70 | GLib.timeout_add(1000, complete) 71 | DhcpClient.querying.remove(self._interface) 72 | return False 73 | elif status: 74 | logging.error(f"dhcp client failed with status code {status}") 75 | self.emit("error-occurred", status) 76 | DhcpClient.querying.remove(self._interface) 77 | return False 78 | else: 79 | return True 80 | -------------------------------------------------------------------------------- /data/ui/services-transfer.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | True 8 | False 9 | 12 10 | 10 11 | 12 12 | 6 13 | 14 | 15 | True 16 | False 17 | start 18 | <b>File Receiving (Object Push)</b> 19 | True 20 | 21 | 22 | 0 23 | 1 24 | 2 25 | 26 | 27 | 28 | 29 | True 30 | False 31 | start 32 | Incoming Folder: 33 | 34 | 35 | 0 36 | 0 37 | 38 | 39 | 40 | 41 | True 42 | False 43 | start 44 | select-folder 45 | Select folder for incoming file transfers 46 | 47 | 48 | 1 49 | 0 50 | 51 | 52 | 53 | 54 | Accept files from trusted devices 55 | True 56 | True 57 | False 58 | start 59 | True 60 | 61 | 62 | 0 63 | 2 64 | 2 65 | 66 | 67 | 68 | 69 | --------------------------------------------------------------------------------