├── 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 |
5 |
--------------------------------------------------------------------------------
/data/icons/hicolor/scalable/actions/blueman-untrust-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
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 |
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 |
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 |
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 |
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 |
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 | [](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 | [](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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------