From e274309d9293777aaaecebccaa29a339a05bd4f9 Mon Sep 17 00:00:00 2001 From: Emanoil Kotsev Date: Mon, 12 Nov 2018 21:18:37 +0100 Subject: [PATCH] Based on KDE3 bluez4 version a TDE bluez5 version was created Signed-off-by: Emanoil Kotsev --- .gitmodules | 0 CMakeLists.txt | 79 ++ COPYING | 340 +++++++ ConfigureChecks.cmake | 59 ++ Doxyfile | 323 +++++++ INSTALL | 1 + README.md | 143 ++- VERSION | 1 + debian/cdbs/debian-tde.mk | 246 +++++ debian/cdbs/versions.pl | 19 + debian/changelog | 12 + debian/compat | 1 + debian/control | 70 ++ debian/copyright | 43 + debian/docs | 1 + debian/libtdebluez-dev.install | 2 + debian/libtdebluez.install | 5 + debian/libtdeobex-dev.install | 2 + debian/libtdeobex.install | 4 + debian/rules | 17 + debian/source/format | 1 + debian/source/options | 6 + debian/tdeblueplugd | 62 ++ debian/tdeblueplugd.desktop | 42 + debian/tdebluez.install | 24 + debian/tdebluez.preinst | 32 + debian/watch | 9 + doc/CMakeLists.txt | 17 + doc/en/CMakeLists.txt | 16 + doc/en/bemused.docbook | 10 + doc/en/components.docbook | 15 + doc/en/concepts.docbook | 85 ++ doc/en/credits.docbook | 55 ++ doc/en/developers.docbook | 25 + doc/en/download.docbook | 16 + doc/en/faq.docbook | 21 + doc/en/handsfree.docbook | 15 + doc/en/index.docbook | 164 ++++ doc/en/installation.docbook | 77 ++ doc/en/introduction.docbook | 24 + doc/en/irmcsynckonnector.docbook | 109 +++ doc/en/kbluelock.docbook | 10 + doc/en/kbtobexsrv.docbook | 49 + doc/en/kbtserialchat.docbook | 44 + doc/en/khciconfig.docbook | 10 + doc/en/links.docbook | 27 + doc/en/news.docbook | 4 + doc/en/othertools.docbook | 31 + doc/en/tdebluez.docbook | 53 ++ doc/en/tdeio_bluetooth.docbook | 41 + doc/en/tdeio_obex.docbook | 61 ++ doc/en/tdeioclient.docbook | 8 + doc/images/adapterconfig.png | Bin 0 -> 42657 bytes doc/images/devices_discovering.png | Bin 0 -> 59132 bytes doc/images/devicesetupwizard.png | Bin 0 -> 22397 bytes doc/images/pairingwithpin.png | Bin 0 -> 37261 bytes doc/images/selectprofiles.png | Bin 0 -> 25843 bytes doc/images/trayicon.png | Bin 0 -> 25880 bytes src/AUTHORS | 1 + src/interfaces/org.bluez.MediaPlayer1.xml | 55 ++ src/interfaces/org.bluez.adapter.xml | 88 ++ src/interfaces/org.bluez.device.xml | 112 +++ src/interfaces/org.bluez.manager.xml | 43 + src/interfaces/org.bluez.obex.Agent1.xml | 23 + src/interfaces/org.bluez.obex.client.xml | 201 ++++ .../org.freedesktop.DBus.ObjectManager.xml | 24 + src/interfaces/org.mpris.MediaPlayer2.xml | 143 +++ src/interfaces/org.tdebluez.agent.xml | 51 ++ src/libtdebluez/CMakeLists.txt | 96 ++ src/libtdebluez/README | 29 + src/libtdebluez/adapterImpl.cpp | 144 +++ src/libtdebluez/adapterImpl.h | 59 ++ src/libtdebluez/btuuids.h | 148 +++ src/libtdebluez/deviceImpl.cpp | 49 + src/libtdebluez/deviceImpl.h | 62 ++ src/libtdebluez/devicemimeconverter.cpp | 147 +++ src/libtdebluez/devicemimeconverter.h | 53 ++ src/libtdebluez/interfaces/CMakeLists.txt | 135 +++ src/libtdebluez/objectmanagerImpl.cpp | 607 ++++++++++++ src/libtdebluez/objectmanagerImpl.h | 177 ++++ src/libtdeobex/CMakeLists.txt | 66 ++ src/libtdeobex/interfaces/CMakeLists.txt | 103 +++ src/libtdeobex/obexobjectmanagerImpl.cpp | 286 ++++++ src/libtdeobex/obexobjectmanagerImpl.h | 94 ++ src/tdebluez-common/CMakeLists.txt | 67 ++ src/tdebluez-common/README | 3 + src/tdebluez-common/dunhandler/CMakeLists.txt | 0 src/tdebluez-common/dunhandler/Makefile.am | 7 + src/tdebluez-common/dunhandler/Makefile.in | 804 ++++++++++++++++ .../dunhandler/configure.in.in | 4 + src/tdebluez-common/dunhandler/dunhandler | 70 ++ .../dunhandler/dunhandler.desktop.in | 10 + src/tdebluez-common/faxhandler/Makefile.am | 7 + src/tdebluez-common/faxhandler/Makefile.in | 804 ++++++++++++++++ .../faxhandler/configure.in.in | 4 + src/tdebluez-common/faxhandler/faxhandler | 10 + .../faxhandler/faxhandler.desktop.in | 9 + src/tdebluez-common/faxhandler/kbtfax | 105 +++ src/tdebluez-common/icons/CMakeLists.txt | 14 + src/tdebluez-common/icons/bluetooth.svg | 86 ++ .../icons/hi128-app-bluetooth.png | Bin 0 -> 5190 bytes .../icons/hi128-app-tdebluez.png | Bin 0 -> 4954 bytes .../icons/hi16-app-bluetooth.png | Bin 0 -> 684 bytes .../icons/hi16-app-tdebluez.png | Bin 0 -> 663 bytes .../icons/hi22-app-bluetooth.png | Bin 0 -> 948 bytes .../icons/hi22-app-media-playback-pause.png | Bin 0 -> 432 bytes .../icons/hi22-app-media-playback-start.png | Bin 0 -> 1378 bytes .../icons/hi22-app-media-playback-stop.png | Bin 0 -> 339 bytes .../icons/hi22-app-media-seek-backward.png | Bin 0 -> 681 bytes .../icons/hi22-app-media-seek-forward.png | Bin 0 -> 471 bytes .../icons/hi22-app-media-skip-backward.png | Bin 0 -> 785 bytes .../icons/hi22-app-media-skip-forward.png | Bin 0 -> 494 bytes .../icons/hi22-app-tdebluelock.png | Bin 0 -> 1239 bytes .../icons/hi22-app-tdebluez.png | Bin 0 -> 875 bytes .../icons/hi32-app-bluetooth.png | Bin 0 -> 1351 bytes .../icons/hi32-app-tdebluelock.png | Bin 0 -> 2019 bytes .../icons/hi32-app-tdebluez.png | Bin 0 -> 1296 bytes .../icons/hi48-app-bluetooth.png | Bin 0 -> 2020 bytes .../icons/hi48-app-tdebluez.png | Bin 0 -> 1973 bytes .../icons/hi64-app-bluetooth.png | Bin 0 -> 2646 bytes .../icons/hi64-app-tdebluez.png | Bin 0 -> 2638 bytes .../icons/hisc-app-tdebluez.svgz | Bin 0 -> 1791 bytes .../icons/inkscape-converter.sh | 14 + src/tdebluez-common/icons/tde-bluetooth.svg | 82 ++ src/tdebluez-common/mimetypes/CMakeLists.txt | 37 + .../mimetypes/av-device-class.desktop | 32 + .../mimetypes/computer-device-class.desktop | 32 + .../mimetypes/dun-profile.desktop | 30 + .../mimetypes/fax-profile.desktop | 31 + .../mimetypes/handsfree-profile.desktop | 29 + .../mimetypes/headset-profile.desktop | 30 + .../mimetypes/health-device-class.desktop | 9 + .../mimetypes/imaging-device-class.desktop | 30 + .../mimetypes/keyboard-device-class.desktop | 29 + .../mimetypes/lan-device-class.desktop | 30 + .../mimetypes/misc-device-class.desktop | 30 + .../mimetypes/mouse-device-class.desktop | 29 + .../mimetypes/obex-ftp-profile.desktop | 29 + .../mimetypes/obex-objectpush-profile.desktop | 32 + .../mimetypes/peripheral-device-class.desktop | 29 + .../mimetypes/phone-device-class.desktop | 32 + .../mimetypes/serial-port-profile.desktop | 29 + .../mimetypes/synchronization-profile.desktop | 31 + .../mimetypes/toy-device-class.desktop | 9 + .../mimetypes/unknown-device-class.desktop | 30 + .../mimetypes/unknown-profile.desktop | 33 + .../mimetypes/wearable-device-class.desktop | 9 + .../org.trinitydesktop.tdebluez.conf | 42 + .../org.trinitydesktop.tdebluez.service | 4 + .../tde-settings-network-bluetooth.directory | 7 + src/tdebluez/CMakeLists.txt | 48 + src/tdebluez/adapterconfig.cpp | 386 ++++++++ src/tdebluez/adapterconfig.h | 83 ++ src/tdebluez/adapterconfigdialog.cpp | 141 +++ src/tdebluez/adapterconfigdialog.h | 70 ++ src/tdebluez/adapterdialog.ui | 346 +++++++ src/tdebluez/application.cpp | 372 ++++++++ src/tdebluez/application.h | 97 ++ src/tdebluez/devicedialog.ui | 335 +++++++ src/tdebluez/devicedialog_ext.ui | 155 ++++ src/tdebluez/devicesetupwizard.cpp | 662 +++++++++++++ src/tdebluez/devicesetupwizard.h | 119 +++ src/tdebluez/devicesetupwizarddialog.ui | 403 ++++++++ src/tdebluez/devicewizard.cpp | 866 ++++++++++++++++++ src/tdebluez/devicewizard.h | 123 +++ src/tdebluez/eventsrc | 397 ++++++++ src/tdebluez/main.cpp | 83 ++ src/tdebluez/mediacontrol.cpp | 181 ++++ src/tdebluez/mediacontrol.h | 66 ++ src/tdebluez/mediactl.ui | 265 ++++++ src/tdebluez/mediaplayer.ui | 253 +++++ src/tdebluez/tdebluez.autostart.desktop | 44 + src/tdebluez/tdebluez.desktop | 61 ++ src/tdebluez/trayicon.cpp | 806 ++++++++++++++++ src/tdebluez/trayicon.h | 160 ++++ src/tdebluezauth/CMakeLists.txt | 47 + src/tdebluezauth/application.cpp | 156 ++++ src/tdebluezauth/application.h | 79 ++ src/tdebluezauth/authdialog.ui | 52 ++ src/tdebluezauth/authorize.cpp | 52 ++ src/tdebluezauth/authorize.h | 47 + src/tdebluezauth/authservice.cpp | 294 ++++++ src/tdebluezauth/authservice.h | 230 +++++ src/tdebluezauth/main.cpp | 77 ++ src/tdebluezauth/pindefdialog.ui | 87 ++ src/tdebluezauth/pindefdialog2.ui | 87 ++ src/tdebluezauth/pindialog.cpp | 58 ++ src/tdebluezauth/pindialog.h | 58 ++ src/tdebluezauth/tdebluezauth.desktop | 36 + src/tdeioclient/CMakeLists.txt | 40 + src/tdeioclient/commandhandler.cpp | 407 ++++++++ src/tdeioclient/commandhandler.h | 60 ++ src/tdeioclient/main.cpp | 73 ++ src/tdeioslave/CMakeLists.txt | 13 + src/tdeioslave/bluetooth/CMakeLists.txt | 40 + src/tdeioslave/bluetooth/bluetooth.protocol | 33 + .../bluetooth/bluetooth_sidebarentry.desktop | 35 + src/tdeioslave/bluetooth/tdeiobluetooth.cpp | 464 ++++++++++ src/tdeioslave/bluetooth/tdeiobluetooth.h | 56 ++ src/tdeioslave/obex/CMakeLists.txt | 38 + src/tdeioslave/obex/README | 91 ++ src/tdeioslave/obex/obex.cpp | 312 +++++++ src/tdeioslave/obex/obex.h | 66 ++ src/tdeioslave/obex/obex_sidebarentry.desktop | 10 + src/tdeioslave/obex/obexftp.protocol | 52 ++ src/tdeioslave/obex/obexopp.protocol | 52 ++ src/tdeioslave/obex/tdeio_obex.cpp | 548 +++++++++++ src/tdeioslave/obex/tdeio_obex.h | 135 +++ 208 files changed, 18727 insertions(+), 1 deletion(-) create mode 100644 .gitmodules create mode 100644 CMakeLists.txt create mode 100644 COPYING create mode 100644 ConfigureChecks.cmake create mode 100644 Doxyfile create mode 100644 INSTALL create mode 100644 VERSION create mode 100644 debian/cdbs/debian-tde.mk create mode 100644 debian/cdbs/versions.pl create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/docs create mode 100644 debian/libtdebluez-dev.install create mode 100644 debian/libtdebluez.install create mode 100644 debian/libtdeobex-dev.install create mode 100644 debian/libtdeobex.install create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 debian/source/options create mode 100644 debian/tdeblueplugd create mode 100644 debian/tdeblueplugd.desktop create mode 100644 debian/tdebluez.install create mode 100644 debian/tdebluez.preinst create mode 100644 debian/watch create mode 100644 doc/CMakeLists.txt create mode 100644 doc/en/CMakeLists.txt create mode 100644 doc/en/bemused.docbook create mode 100644 doc/en/components.docbook create mode 100644 doc/en/concepts.docbook create mode 100644 doc/en/credits.docbook create mode 100644 doc/en/developers.docbook create mode 100644 doc/en/download.docbook create mode 100644 doc/en/faq.docbook create mode 100644 doc/en/handsfree.docbook create mode 100644 doc/en/index.docbook create mode 100644 doc/en/installation.docbook create mode 100644 doc/en/introduction.docbook create mode 100644 doc/en/irmcsynckonnector.docbook create mode 100644 doc/en/kbluelock.docbook create mode 100644 doc/en/kbtobexsrv.docbook create mode 100644 doc/en/kbtserialchat.docbook create mode 100644 doc/en/khciconfig.docbook create mode 100644 doc/en/links.docbook create mode 100644 doc/en/news.docbook create mode 100644 doc/en/othertools.docbook create mode 100644 doc/en/tdebluez.docbook create mode 100644 doc/en/tdeio_bluetooth.docbook create mode 100644 doc/en/tdeio_obex.docbook create mode 100644 doc/en/tdeioclient.docbook create mode 100644 doc/images/adapterconfig.png create mode 100644 doc/images/devices_discovering.png create mode 100644 doc/images/devicesetupwizard.png create mode 100644 doc/images/pairingwithpin.png create mode 100644 doc/images/selectprofiles.png create mode 100644 doc/images/trayicon.png create mode 100644 src/AUTHORS create mode 100644 src/interfaces/org.bluez.MediaPlayer1.xml create mode 100644 src/interfaces/org.bluez.adapter.xml create mode 100644 src/interfaces/org.bluez.device.xml create mode 100644 src/interfaces/org.bluez.manager.xml create mode 100644 src/interfaces/org.bluez.obex.Agent1.xml create mode 100644 src/interfaces/org.bluez.obex.client.xml create mode 100644 src/interfaces/org.freedesktop.DBus.ObjectManager.xml create mode 100644 src/interfaces/org.mpris.MediaPlayer2.xml create mode 100644 src/interfaces/org.tdebluez.agent.xml create mode 100644 src/libtdebluez/CMakeLists.txt create mode 100644 src/libtdebluez/README create mode 100644 src/libtdebluez/adapterImpl.cpp create mode 100644 src/libtdebluez/adapterImpl.h create mode 100644 src/libtdebluez/btuuids.h create mode 100644 src/libtdebluez/deviceImpl.cpp create mode 100644 src/libtdebluez/deviceImpl.h create mode 100644 src/libtdebluez/devicemimeconverter.cpp create mode 100644 src/libtdebluez/devicemimeconverter.h create mode 100644 src/libtdebluez/interfaces/CMakeLists.txt create mode 100644 src/libtdebluez/objectmanagerImpl.cpp create mode 100644 src/libtdebluez/objectmanagerImpl.h create mode 100644 src/libtdeobex/CMakeLists.txt create mode 100644 src/libtdeobex/interfaces/CMakeLists.txt create mode 100644 src/libtdeobex/obexobjectmanagerImpl.cpp create mode 100644 src/libtdeobex/obexobjectmanagerImpl.h create mode 100644 src/tdebluez-common/CMakeLists.txt create mode 100644 src/tdebluez-common/README create mode 100644 src/tdebluez-common/dunhandler/CMakeLists.txt create mode 100644 src/tdebluez-common/dunhandler/Makefile.am create mode 100644 src/tdebluez-common/dunhandler/Makefile.in create mode 100644 src/tdebluez-common/dunhandler/configure.in.in create mode 100644 src/tdebluez-common/dunhandler/dunhandler create mode 100644 src/tdebluez-common/dunhandler/dunhandler.desktop.in create mode 100644 src/tdebluez-common/faxhandler/Makefile.am create mode 100644 src/tdebluez-common/faxhandler/Makefile.in create mode 100644 src/tdebluez-common/faxhandler/configure.in.in create mode 100755 src/tdebluez-common/faxhandler/faxhandler create mode 100644 src/tdebluez-common/faxhandler/faxhandler.desktop.in create mode 100755 src/tdebluez-common/faxhandler/kbtfax create mode 100644 src/tdebluez-common/icons/CMakeLists.txt create mode 100644 src/tdebluez-common/icons/bluetooth.svg create mode 100644 src/tdebluez-common/icons/hi128-app-bluetooth.png create mode 100644 src/tdebluez-common/icons/hi128-app-tdebluez.png create mode 100644 src/tdebluez-common/icons/hi16-app-bluetooth.png create mode 100644 src/tdebluez-common/icons/hi16-app-tdebluez.png create mode 100644 src/tdebluez-common/icons/hi22-app-bluetooth.png create mode 100644 src/tdebluez-common/icons/hi22-app-media-playback-pause.png create mode 100644 src/tdebluez-common/icons/hi22-app-media-playback-start.png create mode 100644 src/tdebluez-common/icons/hi22-app-media-playback-stop.png create mode 100644 src/tdebluez-common/icons/hi22-app-media-seek-backward.png create mode 100644 src/tdebluez-common/icons/hi22-app-media-seek-forward.png create mode 100644 src/tdebluez-common/icons/hi22-app-media-skip-backward.png create mode 100644 src/tdebluez-common/icons/hi22-app-media-skip-forward.png create mode 100644 src/tdebluez-common/icons/hi22-app-tdebluelock.png create mode 100644 src/tdebluez-common/icons/hi22-app-tdebluez.png create mode 100644 src/tdebluez-common/icons/hi32-app-bluetooth.png create mode 100644 src/tdebluez-common/icons/hi32-app-tdebluelock.png create mode 100644 src/tdebluez-common/icons/hi32-app-tdebluez.png create mode 100644 src/tdebluez-common/icons/hi48-app-bluetooth.png create mode 100644 src/tdebluez-common/icons/hi48-app-tdebluez.png create mode 100644 src/tdebluez-common/icons/hi64-app-bluetooth.png create mode 100644 src/tdebluez-common/icons/hi64-app-tdebluez.png create mode 100644 src/tdebluez-common/icons/hisc-app-tdebluez.svgz create mode 100644 src/tdebluez-common/icons/inkscape-converter.sh create mode 100644 src/tdebluez-common/icons/tde-bluetooth.svg create mode 100644 src/tdebluez-common/mimetypes/CMakeLists.txt create mode 100644 src/tdebluez-common/mimetypes/av-device-class.desktop create mode 100644 src/tdebluez-common/mimetypes/computer-device-class.desktop create mode 100644 src/tdebluez-common/mimetypes/dun-profile.desktop create mode 100644 src/tdebluez-common/mimetypes/fax-profile.desktop create mode 100644 src/tdebluez-common/mimetypes/handsfree-profile.desktop create mode 100644 src/tdebluez-common/mimetypes/headset-profile.desktop create mode 100644 src/tdebluez-common/mimetypes/health-device-class.desktop create mode 100644 src/tdebluez-common/mimetypes/imaging-device-class.desktop create mode 100644 src/tdebluez-common/mimetypes/keyboard-device-class.desktop create mode 100644 src/tdebluez-common/mimetypes/lan-device-class.desktop create mode 100644 src/tdebluez-common/mimetypes/misc-device-class.desktop create mode 100644 src/tdebluez-common/mimetypes/mouse-device-class.desktop create mode 100644 src/tdebluez-common/mimetypes/obex-ftp-profile.desktop create mode 100644 src/tdebluez-common/mimetypes/obex-objectpush-profile.desktop create mode 100644 src/tdebluez-common/mimetypes/peripheral-device-class.desktop create mode 100644 src/tdebluez-common/mimetypes/phone-device-class.desktop create mode 100644 src/tdebluez-common/mimetypes/serial-port-profile.desktop create mode 100644 src/tdebluez-common/mimetypes/synchronization-profile.desktop create mode 100644 src/tdebluez-common/mimetypes/toy-device-class.desktop create mode 100644 src/tdebluez-common/mimetypes/unknown-device-class.desktop create mode 100644 src/tdebluez-common/mimetypes/unknown-profile.desktop create mode 100644 src/tdebluez-common/mimetypes/wearable-device-class.desktop create mode 100644 src/tdebluez-common/org.trinitydesktop.tdebluez.conf create mode 100644 src/tdebluez-common/org.trinitydesktop.tdebluez.service create mode 100644 src/tdebluez-common/tde-settings-network-bluetooth.directory create mode 100644 src/tdebluez/CMakeLists.txt create mode 100644 src/tdebluez/adapterconfig.cpp create mode 100644 src/tdebluez/adapterconfig.h create mode 100644 src/tdebluez/adapterconfigdialog.cpp create mode 100644 src/tdebluez/adapterconfigdialog.h create mode 100644 src/tdebluez/adapterdialog.ui create mode 100644 src/tdebluez/application.cpp create mode 100644 src/tdebluez/application.h create mode 100644 src/tdebluez/devicedialog.ui create mode 100644 src/tdebluez/devicedialog_ext.ui create mode 100644 src/tdebluez/devicesetupwizard.cpp create mode 100644 src/tdebluez/devicesetupwizard.h create mode 100644 src/tdebluez/devicesetupwizarddialog.ui create mode 100644 src/tdebluez/devicewizard.cpp create mode 100644 src/tdebluez/devicewizard.h create mode 100644 src/tdebluez/eventsrc create mode 100644 src/tdebluez/main.cpp create mode 100644 src/tdebluez/mediacontrol.cpp create mode 100644 src/tdebluez/mediacontrol.h create mode 100644 src/tdebluez/mediactl.ui create mode 100644 src/tdebluez/mediaplayer.ui create mode 100644 src/tdebluez/tdebluez.autostart.desktop create mode 100644 src/tdebluez/tdebluez.desktop create mode 100644 src/tdebluez/trayicon.cpp create mode 100644 src/tdebluez/trayicon.h create mode 100644 src/tdebluezauth/CMakeLists.txt create mode 100644 src/tdebluezauth/application.cpp create mode 100644 src/tdebluezauth/application.h create mode 100644 src/tdebluezauth/authdialog.ui create mode 100644 src/tdebluezauth/authorize.cpp create mode 100644 src/tdebluezauth/authorize.h create mode 100644 src/tdebluezauth/authservice.cpp create mode 100644 src/tdebluezauth/authservice.h create mode 100644 src/tdebluezauth/main.cpp create mode 100644 src/tdebluezauth/pindefdialog.ui create mode 100644 src/tdebluezauth/pindefdialog2.ui create mode 100644 src/tdebluezauth/pindialog.cpp create mode 100644 src/tdebluezauth/pindialog.h create mode 100644 src/tdebluezauth/tdebluezauth.desktop create mode 100644 src/tdeioclient/CMakeLists.txt create mode 100644 src/tdeioclient/commandhandler.cpp create mode 100644 src/tdeioclient/commandhandler.h create mode 100644 src/tdeioclient/main.cpp create mode 100644 src/tdeioslave/CMakeLists.txt create mode 100644 src/tdeioslave/bluetooth/CMakeLists.txt create mode 100644 src/tdeioslave/bluetooth/bluetooth.protocol create mode 100644 src/tdeioslave/bluetooth/bluetooth_sidebarentry.desktop create mode 100644 src/tdeioslave/bluetooth/tdeiobluetooth.cpp create mode 100644 src/tdeioslave/bluetooth/tdeiobluetooth.h create mode 100644 src/tdeioslave/obex/CMakeLists.txt create mode 100644 src/tdeioslave/obex/README create mode 100644 src/tdeioslave/obex/obex.cpp create mode 100644 src/tdeioslave/obex/obex.h create mode 100644 src/tdeioslave/obex/obex_sidebarentry.desktop create mode 100644 src/tdeioslave/obex/obexftp.protocol create mode 100644 src/tdeioslave/obex/obexopp.protocol create mode 100644 src/tdeioslave/obex/tdeio_obex.cpp create mode 100644 src/tdeioslave/obex/tdeio_obex.h diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e69de29 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5b8ac2c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,79 @@ +################################################# +# +# (C) 2018 Emanoil Kotsev +# deloptes (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +cmake_minimum_required( VERSION 2.8 ) + + +##### general package setup ##################### + +project( tdebluez ) +set( PACKAGE tdebluez ) +set( VERSION R14.1.0 ) + + +##### include essential cmake modules ########### + +include( FindPkgConfig ) +include( CheckIncludeFile ) +include( CheckTypeSize ) +include( CheckCSourceCompiles ) +include( CheckCXXSourceCompiles ) +include( CheckLibraryExists ) + +##### include our cmake modules ################# +include( TDEMacros ) + +##### setup install paths ####################### +include( TDESetupPaths ) +tde_setup_paths( ) + +if( NOT DBUS_SYS_DIR ) + set( DBUS_SYS_DIR ${SYSCONF_INSTALL_DIR}/dbus-1/system.d ) +endif( ) + +##### add apidox targets ############ +add_custom_target(apidox + COMMAND "./generate_apidox" "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" "${HTML_INSTALL_DIR}" "/usr/share/tdebluez/doc/html" + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/cmake/") + +add_custom_target(install-apidox + COMMAND "./install_apidox" "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" "${HTML_INSTALL_DIR}" "${CMAKE_INSTALL_PREFIX}" + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/cmake/") + +##### optional stuff ############################ +option( WITH_ALL_OPTIONS "Enable all optional support" OFF ) + +##### user requested modules #################### +option( BUILD_ALL "Build all" ON ) +option( BUILD_LIBTDEBLUEZ "Build libtdebluez" ON ) +option( BUILD_TDEIOSLAVE "Build tdeioslave" ${BUILD_ALL} ) +option( BUILD_DOC "Build doc" ${BUILD_ALL} ) + +##### configure checks ########################## +# if configure checks need be executed +include( ConfigureChecks.cmake ) + +set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TQT_CXX_FLAGS}" ) +set( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined" ) +#-Wl,--whole-archive +set( CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--no-undefined" ) + +##### activate dependencies ##################### +add_subdirectory( src/tdebluez-common ) +add_subdirectory( src/libtdebluez ) +add_subdirectory( src/libtdeobex ) +add_subdirectory( src/tdeioclient ) +add_subdirectory( src/tdeioslave ) +add_subdirectory( src/tdebluez ) +add_subdirectory( src/tdebluezauth ) + +tde_conditional_add_subdirectory( BUILD_DOC doc ) +tde_conditional_add_subdirectory( BUILD_TRANSLATIONS po ) diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..70190fb --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake new file mode 100644 index 0000000..17b7d27 --- /dev/null +++ b/ConfigureChecks.cmake @@ -0,0 +1,59 @@ +################################################# +# +# (C) 2019 Emanoil Kotsev +# deloptes (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +tde_setup_architecture_flags( ) + +include(TestBigEndian) +test_big_endian(WORDS_BIGENDIAN) + +##### check for gcc visibility support ######### +# FIXME +# This should check for [T]Qt3 visibility support + +if( WITH_GCC_VISIBILITY ) + if( NOT UNIX ) + tde_message_fatal(FATAL_ERROR "\ngcc visibility support was requested, but your system is not *NIX" ) + endif( NOT UNIX ) + set( __KDE_HAVE_GCC_VISIBILITY 1 ) + set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden -fvisibility-inlines-hidden") + set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -fvisibility-inlines-hidden") +endif( ) + + +### +# some if then blocks come here +# to handle submodules +### + +# check for dbus-1 +pkg_search_module( DBUS dbus-1 ) +if( NOT DBUS_FOUND ) + tde_message_fatal( "dbus-1 is required, but was not found on your system" ) +endif( ) + + +# check for dbus-1-tqt +pkg_search_module( DBUS_TQT dbus-1-tqt ) +if( NOT DBUS_TQT_FOUND ) + tde_message_fatal( "dbus-1-tqt is required, but was not found on your system" ) +endif( ) + +# check for msgfmt +if( BUILD_TRANSLATIONS AND NOT DEFINED MSGFMT_EXECUTABLE ) + find_program( MSGFMT_EXECUTABLE msgfmt ) + if( NOT MSGFMT_EXECUTABLE ) + tde_message_fatal( "msgfmt program is required, but was not found on your system" ) + endif( ) +endif( ) + +# required stuff +find_package( TQt ) +find_package( TDE ) diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000..4e7f6cc --- /dev/null +++ b/Doxyfile @@ -0,0 +1,323 @@ +# Doxyfile 1.5.7.1-KDevelop + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = tdebluez +PROJECT_NUMBER = 1 +OUTPUT_DIRECTORY = +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = `pwd` +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +TQT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +EXTENSION_MAPPING = +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +TYPEDEF_HIDES_STRUCT = NO +SYMBOL_CACHE_SIZE = 0 +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = NO +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = src +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py \ + *.f90 \ + *.f \ + *.vhd \ + *.vhdl \ + *.C \ + *.CC \ + *.C++ \ + *.II \ + *.I++ \ + *.H \ + *.HH \ + *.H++ \ + *.CS \ + *.PHP \ + *.PHP3 \ + *.M \ + *.MM \ + *.PY \ + *.F90 \ + *.F \ + *.VHD \ + *.VHDL \ + *.C \ + *.H \ + *.tlh \ + *.diff \ + *.patch \ + *.moc \ + *.xpm \ + *.dox +RECURSIVE = yes +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +HTML_DYNAMIC_SECTIONS = NO +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_TQHP = NO +TQCH_FILE = +TQHP_NAMESPACE = +TQHP_VIRTUAL_FOLDER = doc +TQHP_CUSTOM_FILTER_NAME= +TQHP_CUST_FILTER_ATTRS = +TQHP_SECT_FILTER_ATTRS = +TQHG_LOCATION = +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NONE +TREEVIEW_WIDTH = 250 +FORMULA_FONTSIZE = 10 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = YES +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = yes +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = testgenbt.tag +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +MSCGEN_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +DOT_FONTNAME = FreeSans +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 1000 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/INSTALL @@ -0,0 +1 @@ + diff --git a/README.md b/README.md index 03cb79c..e4139ab 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,143 @@ -# tdebluez +# Overview + +A set of applications to provide modern Bluetooth functionality to TDE. + +Originally it was introduced around 2008 in SUSE, but it was not included in the main KDE3 set of applications. + +Due to the major API change from Bluez4 to Bluez5 in Linux it was necessary to completely rewrite most of the applications while adding new and removing old parts of the functionality that were obsoleted. + +Lack of devices for testing and differences in hardware standards make testing even more difficult and suggest unexpected behavior and issues. Please test and report back so that we can improve the application and its components. + + +# Applications and libraries + +## tdebluez +This is the main application that provides the main functionality. + +* Requires at least bluez 5.43 + +* It provides agent for pairing. + +* Allows to configure bluetooth adapters and devices. + +* User must be member of group netdev for /dev/rfkill access. This is required to make BT adapter switch on/off to work. + +* pulseaudio-module-bluetooth needs to be installed for a2dp to work + +* Tray icon + + ![Tray Icon](doc/images/trayicon.png) + +* Adapter (device) configuration + + ![Adapter (device) configuration](doc/images/adapterconfig.png) + +* Discovering devices + + ![Discovering devices](doc/images/devices_discovering.png) + +* Device setup wizard + + ![Device setup wizard](doc/images/devicesetupwizard.png) + +* Pairing agent implements [BlueZ D-Bus Agent API](https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/agent-api.txt) (example pairing with PIN) + + ![Pairing with PIN](doc/images/pairingwithpin.png) + +* Connect services (profiles) + + ![Connect services (profiles)](doc/images/selectprofiles.png) + + +## libtdebluez +Almost complete rewrite for BlueZ 5 D-Bus API. +This is the main library providing the core functionality. + +## libtdeobex + +New for BlueZ 5 D-Bus API. + + +## tdebluezauth +This is helper application that can be run stand alone, but it is usually started from tdebluez. +It provides agent for authentication utilizing several DBus interfaces. + +## bluez-obexd +This can be seen as third party application that can be started as stand alone application or by tdebluez. +It provides Obex FTP, OPP, Message Access, PSE. + +* FTP is working per default in ~/Download directory. +If you want to change this, open .trinity/share/config/tdebluezrc and add/modify entry + downloadDir=Downloads +You can use absolute or relative path. Both absolute and relative can be used, but relative paths are assumed to be relative to user $HOME folder. Default $HOME/Downloads. + +## openobex +This is third party library providing functionality for SyncML like used in SyncEvolution. Please, note that only v1.5 works. v1.7 is broken. + +# Configuration + +The configuration file is `~/.trinity/share/config/tdebluezrc` + +Here is an example + +``` +[20:xx:xx:xx:xx:xx] +profile=0000110a-0000-1000-8000-00805f9b34fb,0000111f-0000-1000-8000-00805f9b34fb + +[38:xx:xx:xx:xx:xx] +profile=0000110a-0000-1000-8000-00805f9b34fb,0000111f-0000-1000-8000-00805f9b34fb + +[3A:xx:xx:xx:xx:xx] +profile=0000110b-0000-1000-8000-00805f9b34fb + +[50:xx:xx:xx:xx:xx] +profile=0000110b-0000-1000-8000-00805f9b34fb,00001108-0000-1000-8000-00805f9b34fb + +[55:xx:xx:xx:xx:xx] +profile=0000110b-0000-1000-8000-00805f9b34fb,0000111e-0000-1000-8000-00805f9b34fb + + +[General] +authAgent=true +autoStart=true +downloadDir=Downloads +obexSrv=true +obexSrvExe[$e]=/usr/libexec/bluetooth/obexd + +[Notification Messages] +systemtrayquitTDEBluetooth=false +``` + +# Known issues +Please report back your experience, so that we can improve the code. + +* At the moment support for only one adapter was tested. + +* Due to lack of HID devices, the code was tested only with +** mobile phones (Android, Sailfish OS) +** headset +** head phones + +* The ioslaves support only file/directory listing. + +# Improvements +## 14.1 TODO List +1. fix the Async error while pairing (and connecting) [**done**] + * Async error is OK and correct + * some profiles are not compatible with other profiles (can not be connected simultaniusly) + * Connecting now works with more than one device + * it can connect one device at a time + * some profiles are allowed to connect only once +2. flip the mouse press button on the tray icon [**done**] +3. fix the wizard when pairing (test with Android phone and non +Android also with Headsets) [**done**] +4. add some logic so that sub-processes are killed in case tdebluez crashes + +After the 14.1 release + +5. add obexFTP/OPP support +6. add support for multiple adapters +7. add autoconnect option +8. Port the UUID logic from https://codebrowser.dev/qt5/qtconnectivity/src/bluetooth/qbluetoothuuid.h.html + diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..ae44f17 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +tdebluez version 0.1 diff --git a/debian/cdbs/debian-tde.mk b/debian/cdbs/debian-tde.mk new file mode 100644 index 0000000..f36e352 --- /dev/null +++ b/debian/cdbs/debian-tde.mk @@ -0,0 +1,246 @@ +# -*- mode: makefile; coding: utf-8 -*- +# Copyright © 2003 Christopher L Cheney +# Copyright © 2019 TDE Team +# Description: A class for TDE packages; sets TDE environment variables, etc +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2, or (at +# your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +# 02111-1307 USA. + +ifndef _cdbs_bootstrap +_cdbs_scripts_path ?= /usr/lib/cdbs +_cdbs_rules_path ?= /usr/share/cdbs/1/rules +_cdbs_class_path ?= /usr/share/cdbs/1/class +endif + +ifndef _cdbs_class_debian-qt-kde +_cdbs_class_debian-qt-kde := 1 + +# for dh_icons +CDBS_BUILD_DEPENDS := $(CDBS_BUILD_DEPENDS), debhelper (>= 5.0.7ubuntu4) + +# Note: This _must_ be included before autotools.mk, or it won't work. +common-configure-arch common-configure-indep:: debian/stamp-cvs-make +debian/stamp-cvs-make: +ifndef _cdbs_class_cmake + cp -Rp /usr/share/aclocal/libtool.m4 admin/libtool.m4.in +ifneq "$(wildcard /usr/share/libtool/config/ltmain.sh)" "" + cp -Rp /usr/share/libtool/config/ltmain.sh admin/ltmain.sh +endif +ifneq "$(wildcard /usr/share/libtool/build-aux/ltmain.sh)" "" + cp -Rp /usr/share/libtool/build-aux/ltmain.sh admin/ltmain.sh +endif + $(MAKE) -C $(DEB_SRCDIR) -f admin/Makefile.common dist; +endif + touch debian/stamp-cvs-make + +include $(_cdbs_rules_path)/buildcore.mk$(_cdbs_makefile_suffix) + +ifdef _cdbs_tarball_dir +DEB_BUILDDIR = $(_cdbs_tarball_dir)/obj-$(DEB_BUILD_GNU_TYPE) +else +DEB_BUILDDIR = obj-$(DEB_BUILD_GNU_TYPE) +endif + +ifndef _cdbs_class_cmake +include $(_cdbs_class_path)/autotools.mk$(_cdbs_makefile_suffix) +endif + +ifdef _cdbs_class_cmake +ifneq "$(wildcard /usr/bin/ninja)" "" +MAKE = ninja -v +DEB_MAKE_ENVVARS += DESTDIR=$(DEB_DESTDIR) +DEB_MAKE_INSTALL_TARGET = install +DEB_CMAKE_NORMAL_ARGS += -GNinja +endif +endif + +ifndef _cdbs_rules_patchsys_quilt +DEB_PATCHDIRS := debian/patches/common debian/patches +endif + +export kde_cgidir = \$${libdir}/cgi-bin +export kde_confdir = \$${sysconfdir}/trinity +export kde_htmldir = \$${datadir}/doc/tde/HTML + +DEB_KDE_ENABLE_FINAL := yes +DEB_INSTALL_DOCS_ALL := + +DEB_DH_MAKESHLIBS_ARGS_ALL := -V +DEB_SHLIBDEPS_INCLUDE = $(foreach p,$(PACKAGES_WITH_LIBS),debian/$(p)/usr/lib) + +DEB_AC_AUX_DIR = $(DEB_SRCDIR)/admin +DEB_CONFIGURE_INCLUDEDIR = "\$${prefix}/include" +DEB_COMPRESS_EXCLUDE = .dcl .docbook -license .tag .sty .el + +# The default gzip compressor has been changed in dpkg >= 1.17.0. +deb_default_compress = $(shell LANG=C dpkg-deb --version | head -n1 | \ + sed -e "s|.*version ||" -e "s| .*||" | \ + xargs -r dpkg --compare-versions 1.17.0 lt \ + && echo xz || echo gzip) +ifeq ($(deb_default_compress),gzip) +DEB_DH_BUILDDEB_ARGS += -- -Z$(shell dpkg-deb --help | grep -q ":.* xz[,.]" \ + && echo xz || echo bzip2) +endif + +ifeq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + cdbs_treat_me_gently_arches := arm m68k alpha ppc64 armel armeb + ifeq (,$(filter $(DEB_HOST_ARCH_CPU),$(cdbs_treat_me_gently_arches))) + cdbs_kde_enable_final = $(if $(DEB_KDE_ENABLE_FINAL),--enable-final,) + else + cdbs_kde_enable_final = + endif +endif + +ifneq (,$(filter nostrip,$(DEB_BUILD_OPTIONS))) + cdbs_kde_enable_final = + cdbs_kde_enable_debug = --enable-debug=yes +else + cdbs_kde_enable_debug = --disable-debug +endif + +ifneq (,$(filter debug,$(DEB_BUILD_OPTIONS))) + cdbs_kde_enable_debug = --enable-debug=full +endif + +DEB_BUILD_PARALLEL ?= true + +cdbs_configure_flags += \ + --with-qt-dir=/usr/share/qt3 \ + --disable-rpath \ + --with-xinerama \ + $(cdbs_kde_enable_final) \ + $(cdbs_kde_enable_debug) + + +# This is a convenience target for calling manually. +# It's not part of the build process. +buildprep: clean apply-patches +ifndef _cdbs_class_cmake + $(MAKE) -f admin/Makefile.common dist +endif + debian/rules clean + +.tdepkginfo: + echo "# TDE package information" >.tdepkginfo + dpkg-parsechangelog | sed -n "s|^Source: |Name: |p" >>.tdepkginfo + dpkg-parsechangelog | sed -n "s|^Version: |Version: |p" >>.tdepkginfo + date +"DateTime: %m/%d/%Y %H:%M" -u -d "$$(dpkg-parsechangelog | sed -n 's|^Date: ||p')" >>.tdepkginfo + +post-patches:: .tdepkginfo + +common-build-arch:: debian/stamp-man-pages +debian/stamp-man-pages: + if ! test -d debian/man/out; then mkdir -p debian/man/out; fi + for f in $$(find debian/man -name '*.sgml'); do \ + docbook-to-man $$f > debian/man/out/`basename $$f .sgml`.1; \ + done + for f in $$(find debian/man -name '*.man'); do \ + soelim -I debian/man $$f \ + > debian/man/out/`basename $$f .man`.`head -n1 $$f | awk '{print $$NF}'`; \ + done + touch debian/stamp-man-pages + +common-binary-indep:: + ( set -e; \ + tmpf=`mktemp debian/versions.XXXXXX`; \ + perl debian/cdbs/versions.pl >$$tmpf; \ + for p in $(DEB_INDEP_PACKAGES); do \ + cat $$tmpf >>debian/$$p.substvars; \ + done; \ + rm -f $$tmpf ) + +common-binary-arch:: + ( set -e; \ + tmpf=`mktemp debian/versions.XXXXXX`; \ + perl debian/cdbs/versions.pl >$$tmpf; \ + for p in $(DEB_ARCH_PACKAGES); do \ + cat $$tmpf >>debian/$$p.substvars; \ + done; \ + rm -f $$tmpf ) + # update multi-arch path in install files + ls -d debian/* | \ + grep -E "(install|links)$$" | \ + while read a; do \ + [ -d $$a ] || [ -f $$a.arch ] || \ + ! grep -q "\$$(DEB_HOST_MULTIARCH)" $$a || \ + sed -i.arch "s|\$$(DEB_HOST_MULTIARCH)|$(DEB_HOST_MULTIARCH)|g" $$a; \ + done + +clean:: + rm -rf debian/man/out + -rmdir debian/man + rm -f debian/stamp-man-pages + rm -rf debian/shlibs-check + # revert multi-arch path in install files + ls -d debian/* | \ + grep -E "(install|links)$$" | \ + while read a; do \ + [ ! -f $$a.arch ] || \ + mv $$a.arch $$a; \ + done + +$(patsubst %,binary-install/%,$(DEB_PACKAGES)) :: binary-install/%: + if test -x /usr/bin/dh_icons; then dh_icons -p$(cdbs_curpkg) $(DEB_DH_ICONCACHE_ARGS); fi + if test -x /usr/bin/dh_desktop; then dh_desktop -p$(cdbs_curpkg) $(DEB_DH_DESKTOP_ARGS); fi + if test -e debian/$(cdbs_curpkg).lintian; then \ + install -p -D -m644 debian/$(cdbs_curpkg).lintian \ + debian/$(cdbs_curpkg)/usr/share/lintian/overrides/$(cdbs_curpkg); \ + fi + if test -e debian/$(cdbs_curpkg).presubj; then \ + install -p -D -m644 debian/$(cdbs_curpkg).presubj \ + debian/$(cdbs_curpkg)/usr/share/bug/$(cdbs_curpkg)/presubj; \ + fi + +binary-install/$(DEB_SOURCE_PACKAGE)-doc-html:: + set -e; \ + for doc in `cd $(DEB_DESTDIR)/opt/trinity/share/doc/tde/HTML/en; find . -name index.docbook`; do \ + pkg=$${doc%/index.docbook}; pkg=$${pkg#./}; \ + echo Building $$pkg HTML docs...; \ + mkdir -p $(CURDIR)/debian/$(DEB_SOURCE_PACKAGE)-doc-html/opt/trinity/share/doc/tde/HTML/en/$$pkg; \ + cd $(CURDIR)/debian/$(DEB_SOURCE_PACKAGE)-doc-html/opt/trinity/share/doc/tde/HTML/en/$$pkg; \ + /opt/trinity/bin/meinproc $(DEB_DESTDIR)/opt/trinity/share/doc/tde/HTML/en/$$pkg/index.docbook; \ + done + for pkg in $(DOC_HTML_PRUNE) ; do \ + rm -rf debian/$(DEB_SOURCE_PACKAGE)-doc-html/opt/trinity/share/doc/tde/HTML/en/$$pkg; \ + done + +common-build-indep:: debian/stamp-kde-apidox +debian/stamp-kde-apidox: + $(if $(DEB_KDE_APIDOX),+$(DEB_MAKE_INVOKE) apidox) + touch $@ + +common-install-indep:: common-install-kde-apidox +common-install-kde-apidox:: + $(if $(DEB_KDE_APIDOX),+DESTDIR=$(DEB_DESTDIR) $(DEB_MAKE_INVOKE) install-apidox) + +cleanbuilddir:: + -$(if $(call cdbs_streq,$(DEB_BUILDDIR),$(DEB_SRCDIR)),,rm -rf $(DEB_BUILDDIR)) + +clean:: +ifndef _cdbs_class_cmake + if test -n "$(DEB_KDE_CVS_MAKE)" && test -d $(DEB_SRCDIR); then \ + cd $(DEB_SRCDIR); \ + find . -name Makefile.in -print | \ + xargs --no-run-if-empty rm -f; \ + rm -f Makefile.am acinclude.m4 aclocal.m4 config.h.in \ + configure configure.files configure.in stamp-h.in \ + subdirs; \ + fi +endif + rm -f .tdepkginfo + rm -f debian/stamp-kde-apidox + rm -f debian/stamp-cvs-make + +endif diff --git a/debian/cdbs/versions.pl b/debian/cdbs/versions.pl new file mode 100644 index 0000000..1b110f7 --- /dev/null +++ b/debian/cdbs/versions.pl @@ -0,0 +1,19 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +my $version = `dpkg-parsechangelog | awk '/^Version/ {print \$2}'`; +my ($version3, $version3_next); +my ($version2, $version2_next); + +($version3 = $version) =~ s/^(([^.]+\.){2}[^.+~-]+)[.+~-]?[^-]*-[^-]+$/$1/; +($version2 = $version3) =~ s/\.[^.]+$//; + +($version3_next = $version3) =~ s/(?<=\.)(\d+)[a-z]?$/($1+1)/e; +($version2_next = $version2) =~ s/(?<=\.)(\d+)$/($1+1)/e; + +print "TDE-Version3=$version3\n"; +print "TDE-Version2=$version2\n"; +print "TDE-Next-Version3=$version3_next\n"; +print "TDE-Next-Version2=$version2_next\n"; diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..aac5c9d --- /dev/null +++ b/debian/changelog @@ -0,0 +1,12 @@ +tdebluez (4:14.1.0-0debian10.1.1+eko1) unstable; urgency=low + + * Build for buster. + + -- Emanoil Kotsev Sat, 02 Nov 2019 22:00:00 +0100 + +tdebluez (4:14.1.0-0debian9.0.5+eko1) unstable; urgency=low + + * Rework of Dbus interface to Bluez5 for TDE 14.1. + + -- Emanoil Kotsev Sat, 29 Sep 2018 22:00:00 +0100 + diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +9 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..ef9da49 --- /dev/null +++ b/debian/control @@ -0,0 +1,70 @@ +Source: tdebluez +Section: tde +Priority: optional +Maintainer: Debian TDE Team +Uploaders: ... +Build-Depends: cdbs, debhelper (>= 9~), quilt, cmake-trinity, ninja-build, tdelibs14-trinity-dev, libdbus-1-tqt-dev, libdbus-1-3 (>= 1.10.26) +Standards-Version: 3.8.0 +#Vcs-Svn: svn://svn.debian.org/pkg-kde/kde-extras/kdebluetooth/trunk/ +#Vcs-Browser: http://svn.debian.org/wsvn/pkg-kde/kde-extras/kdebluetooth/trunk/ + +Package: tdebluez +Section: tde +Architecture: any +Depends: ${shlibs:Depends}, bluez (>= 5.43), bluez-obexd (>= 5.43), tdelibs14-trinity, libdbus-1-tqt +Suggests: pulseaudio-module-bluetooth +Description: TDE Bluetooth Framework + The TDE Bluetooth Framework is a set of tools built on top of Linux' Bluetooth + stack BlueZ5. It provides easy access to the most common Bluetooth profiles and + makes data exchange with Bluetooth devices like phones and PDAs as + straightforward as possible. + . + Features: + * tdebluez -- a tray applet to handle incoming inquiries and control + adapters and devices + * tdebluezauth -- authentication agent + * tdeioclient -- cli to obex + +Package: libtdebluez +Architecture: any +Section: libs +Replaces: libtdebluez (<< 0.1) +Depends: ${shlibs:Depends}, bluez (>= 5.43) +Description: Bluetooth library for TDE + This package is part of the TDE Bluetooth Framework. + It contains a Bluetooth library for TDE. + . + See the 'tdebluez' package for more informations. + +Package: libtdeobex +Architecture: any +Section: libs +Replaces: libtdeobex (<< 0.1) +Depends: ${shlibs:Depends}, bluez (>= 5.43), bluez-obexd (>= 5.43) +Description: Obex library for TDE + This package is part of the TDE Bluetooth Framework. + It contains a Bluetooth library for TDE. + . + See the 'tdebluez' package for more informations. + +Package: libtdebluez-dev +Architecture: any +Section: libdevel +Depends: libtdebluez (= ${binary:Version}) +Replaces: libtdebluez-dev (<< 0.1) +Description: development files for libtdebluez + This package is part of the TDE Bluetooth Framework. + It contains the development files for libtdebluez. + . + See the 'tdebluez' package for more informations. + +Package: libtdeobex-dev +Architecture: any +Section: libdevel +Depends: libtdebluez (= ${binary:Version}) +Replaces: libtdebluez-dev (<< 0.1) +Description: development files for libtdebluez + This package is part of the TDE Bluetooth Framework. + It contains the development files for libtdebluez. + . + See the 'tdebluez' package for more informations. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..7fc5c09 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,43 @@ +This package was debianized by Emanoil Kotsev +on Fri, 19 Sep 2003 18:47:31 +0200. + +It was downloaded from http://bluetooth.kmobiletools.org + +Ported to TDE by Emanoil Kotsev +on Tue, 22 Nov 2016 17:23:21 +0100. + +Upstream Authors: + + Emanoil Kotsev + +Copyright: + + (C) 2018 Emanoil Kotsev + +License: + + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this package; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +On Debian systems, the complete text of the GNU General +Public License can be found in `/usr/share/common-licenses/GPL'. + +The documentations provided with kdebluetooth is distributed under +the GNU Free Documentation License (FDL). They are considered free with +regards to the Debian Free Software Guidelines (DFSG) because they don't +contain any unmodifiable parts (invariant sections). + +On Debian systems, the complete text of the GNU Free Documentation +License can be found in `/usr/share/common-licenses/GFDL'. + diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..b43bf86 --- /dev/null +++ b/debian/docs @@ -0,0 +1 @@ +README.md diff --git a/debian/libtdebluez-dev.install b/debian/libtdebluez-dev.install new file mode 100644 index 0000000..1593ff2 --- /dev/null +++ b/debian/libtdebluez-dev.install @@ -0,0 +1,2 @@ +opt/trinity/share/cmake/libtdebluez.cmake +opt/trinity/include/tdebluez/* diff --git a/debian/libtdebluez.install b/debian/libtdebluez.install new file mode 100644 index 0000000..cba8c73 --- /dev/null +++ b/debian/libtdebluez.install @@ -0,0 +1,5 @@ +opt/trinity/lib/libtdebluez.so.0 +opt/trinity/lib/libtdebluez.so.0.0.1 +opt/trinity/lib/libtdebluez.so +opt/trinity/lib/libtdebluez.la +etc/trinity/dbus-1/system.d/org.trinitydesktop.tdebluez.conf /etc/dbus-1/system.d/ diff --git a/debian/libtdeobex-dev.install b/debian/libtdeobex-dev.install new file mode 100644 index 0000000..0e91749 --- /dev/null +++ b/debian/libtdeobex-dev.install @@ -0,0 +1,2 @@ +opt/trinity/share/cmake/libtdeobex.cmake +opt/trinity/include/tdeobex/* diff --git a/debian/libtdeobex.install b/debian/libtdeobex.install new file mode 100644 index 0000000..65642ce --- /dev/null +++ b/debian/libtdeobex.install @@ -0,0 +1,4 @@ +opt/trinity/lib/libtdeobex.la +opt/trinity/lib/libtdeobex.so +opt/trinity/lib/libtdeobex.so.0 +opt/trinity/lib/libtdeobex.so.0.0.1 diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..a6797f1 --- /dev/null +++ b/debian/rules @@ -0,0 +1,17 @@ +#!/usr/bin/make -f + +include /usr/share/cdbs/1/rules/debhelper.mk +include /usr/share/cdbs/1/class/cmake.mk +include debian/cdbs/debian-tde.mk + +DEB_CMAKE_EXTRA_FLAGS := \ + -DCMAKE_EXPORT_COMPILE_COMMANDS="ON" \ + -DCMAKE_INSTALL_PREFIX="/opt/trinity" \ + -DCONFIG_INSTALL_DIR="/etc/trinity" \ + -DSYSCONF_INSTALL_DIR="/etc/trinity" \ + -DXDG_MENU_INSTALL_DIR="/etc/xdg/menus" \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_VERBOSE_MAKEFILE="ON" \ + -DCMAKE_SKIP_RPATH="OFF" \ + -DBUILD_ALL="ON" \ + -DWITH_ALL_OPTIONS="ON" diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/debian/source/options b/debian/source/options new file mode 100644 index 0000000..72f1f54 --- /dev/null +++ b/debian/source/options @@ -0,0 +1,6 @@ +# Use xz instead of gzip +compression = "xz" +compression-level = 9 + +# Don't run differences +diff-ignore = .* diff --git a/debian/tdeblueplugd b/debian/tdeblueplugd new file mode 100644 index 0000000..7d3db40 --- /dev/null +++ b/debian/tdeblueplugd @@ -0,0 +1,62 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +""" +Copyright (C) 2007 Achim Bohnet + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" + +import sys +from PyQt4 import QtCore, QtGui +import dbus +#import dbus.mainloop.tqt3 +import dbus.mainloop.qt +import distutils.spawn + +tdebtcmd = [ 'tdebluetooth' ] +quitprogs = [ 'tdebluetooth', 'tdebluemon', 'tdeinputwizard' ] # FIXME: quit tdebluelock too? + + +app = TQtGui.TQApplication(sys.argv) +if app.isSessionRestored(): + sys.exit(1) + +dbus.mainloop.qt.DBusQtMainLoop(set_as_default=True) +bus = dbus.SystemBus() + +try: + manager = dbus.Interface(bus.get_object('org.bluez', '/org/bluez'), 'org.bluez.Manager') +except: + print "Unable to connect to bluez." + sys.exit(1) + +if len(manager.ListAdapters()): + print "# of devices at startup:", len(manager.ListAdapters()) + distutils.spawn.spawn(kbtcmd) +else: + print "No BT device found" + + +def slotAdapterAdded(device): + print "bt dev added:", device, "# of devices:", len(manager.ListAdapters()) + distutils.spawn.spawn(kbtcmd) + +def slotAdapterRemoved(device): + print "bt dev removed:", device, "# num of devices:", len(manager.ListAdapters()) + if len(manager.ListAdapters()) == 0: + for p in quitprogs: + print "exiting:", p, " ..." + try: + distutils.spawn.spawn(['dcop', p, 'MainApplication-Interface', 'quit']) + except: + pass + +manager.connect_to_signal("AdapterAdded", slotAdapterAdded) +manager.connect_to_signal("AdapterRemoved", slotAdapterRemoved) + +print "waiting for bt device (un)plug events ..." +app.exec_() +#sys.exit(app.exec_()) diff --git a/debian/tdeblueplugd.desktop b/debian/tdeblueplugd.desktop new file mode 100644 index 0000000..95d37e3 --- /dev/null +++ b/debian/tdeblueplugd.desktop @@ -0,0 +1,42 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=TDEBluetoothD +Name[sv]=TDEbluetoothd +Name[ta]=Kபுலுடுத்D +Name[xx]=xxTDEBluetoothDxx +GenericName=TDE Bluetooth Daemon +GenericName[ar]=مراقب TDE Bluetooth +GenericName[bg]=Демон за Bluetooth в TDE +GenericName[br]=Diaoul TDE Bluetooth +GenericName[bs]=TDE Bluetooth daemon +GenericName[ca]=Dimoni Bluetooth de TDE +GenericName[cs]=Bluetooth démon pro TDE +GenericName[da]=TDE Bluetooth-dæmon +GenericName[de]=TDE Bluetooth-Daemon +GenericName[el]=Ο δαίμονας Bluetooth του TDE +GenericName[es]=Daemon Bluetooth de TDE +GenericName[et]=TDE Bluetoothi deemon +GenericName[fr]=Démon TDE Bluetooth +GenericName[gl]=Servizo Bluetooth de TDE +GenericName[it]=Demone Bluetooth di TDE +GenericName[ja]=TDE Bluetooth デーモン +GenericName[ka]=TDE ბლუთუს დემონი +GenericName[nl]=TDE Bluetooth-daemon +GenericName[pa]=TDE ਬਲਿਊਟੁੱਥ ਡਾਈਮੋਨ +GenericName[pl]=Demon Bluetooth dla TDE +GenericName[pt]=Serviço Bluetooth do TDE +GenericName[ru]=Служба TDE Bluetooth +GenericName[sr]=Bluetooth демон за TDE +GenericName[sr@Latn]=Bluetooth demon za TDE +GenericName[sv]=TDE-Blåtandsdemon +GenericName[ta]=TDE புலுடுத் டேமொன் +GenericName[tr]=TDE Bluetooth Hizmeti +GenericName[xx]=xxTDE Bluetooth Daemonxx +Exec=tdeblueplugd +Icon=TDEbluetooth +Type=Application +DocPath=TDEbluetooth/index.html +Terminal=false +X-TDE-autostart-after=panel +X-TDE-StartupNotify=false +X-TDE-autostart-condition=tdebluezrc:General:AutoStart:true diff --git a/debian/tdebluez.install b/debian/tdebluez.install new file mode 100644 index 0000000..2cb7792 --- /dev/null +++ b/debian/tdebluez.install @@ -0,0 +1,24 @@ +opt/trinity/bin/tdebluez +opt/trinity/bin/tdebluezauth +opt/trinity/bin/tdeioclient +opt/trinity/lib/trinity/tdebluez.so +opt/trinity/lib/trinity/tdebluez.la +opt/trinity/lib/libtdeinit_tdebluez.so +opt/trinity/lib/libtdeinit_tdebluez.la +opt/trinity/lib/trinity/tdeio_bluetooth.so +opt/trinity/lib/trinity/tdeio_bluetooth.la +opt/trinity/lib/trinity/tdeio_obex.so +opt/trinity/lib/trinity/tdeio_obex.la +opt/trinity/share/apps/tdebluez/icons/* +opt/trinity/share/doc/tde/HTML/* +opt/trinity/share/apps/tdebluez/eventsrc +opt/trinity/share/apps/tdebluez/tde-settings-network-bluetooth.directory +opt/trinity/share/applications/tde/tdebluez.desktop +opt/trinity/share/applications/tde/tdebluezauth.desktop +opt/trinity/share/autostart/tdebluez.autostart.desktop +opt/trinity/share/mimelnk/bluetooth/* +opt/trinity/share/services/bluetooth.protocol +opt/trinity/share/services/obexftp.protocol +opt/trinity/share/services/obexopp.protocol +opt/trinity/share/apps/konqsidebartng/virtual_folders/services/bluetooth_sidebarentry.desktop +opt/trinity/share/apps/konqsidebartng/virtual_folders/services/obex_sidebarentry.desktop diff --git a/debian/tdebluez.preinst b/debian/tdebluez.preinst new file mode 100644 index 0000000..3a28f8c --- /dev/null +++ b/debian/tdebluez.preinst @@ -0,0 +1,32 @@ +#!/bin/sh +exit 0 +# Remove a no-longer used conffile +rm_conffile () +{ + PKGNAME="$1" + CONFFILE="$2" + + if [ -e "$CONFFILE" ]; then + md5sum="`md5sum \"$CONFFILE\" | sed -e \"s/ .*//\"`" + old_md5sum="`dpkg-query -W -f='${Conffiles}' $PKGNAME | sed -n -e \"\\\\' $CONFFILE'{s/ obsolete$//;s/.* //p}\"`" + if [ "$md5sum" != "$old_md5sum" ]; then + echo "Obsolete conffile $CONFFILE has been modified by you." + echo "Saving as $CONFFILE.dpkg-bak ..." + mv -f "$CONFFILE" "$CONFFILE".dpkg-bak + else + echo "Removing obsolete conffile $CONFFILE ..." + rm -f "$CONFFILE" + fi + fi +} + +# Last version that might have the obsolete conffile. +LASTVERSION="0.1" +case "$1" in install | upgrade) + echo "FIXME: tdebluez.preinstall Last version that might have the obsolete conffile" +# if dpkg --compare-versions "$2" le "$LASTVERSION"; then +# rm_conffile tdebluetooth /etc/kde3/obexrc +# fi +esac + +#DEBHELPER# diff --git a/debian/watch b/debian/watch new file mode 100644 index 0000000..61fdcd4 --- /dev/null +++ b/debian/watch @@ -0,0 +1,9 @@ +# See uscan(1) for format + +# Compulsory line, this is a version 3 file +version=3 + +# Uncomment to find new files on sourceforge, for debscripts >= 2.9 +#opts=uversionmangle=s/_/-/,dversionmangle=s/~/-/ \ +# http://cryptomilch.de/~dgollub/kdebluetooth/kdebluetooth-(.*)\.tar\.bz2 debian svn-upgrade +# http://sf.net/kde-bluetooth/kdebluetooth-(.*)\.tar\.gz debian svn-upgrade diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt new file mode 100644 index 0000000..668d592 --- /dev/null +++ b/doc/CMakeLists.txt @@ -0,0 +1,17 @@ +################################################# +# +# (C) 2018 Emanoil Kotsev +# deloptes (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +#KDE_LANG = en +#KDE_DOCS = AUTO +#SUBDIRS = $(AUTODIRS) + +tde_auto_add_subdirectories() + diff --git a/doc/en/CMakeLists.txt b/doc/en/CMakeLists.txt new file mode 100644 index 0000000..9456e01 --- /dev/null +++ b/doc/en/CMakeLists.txt @@ -0,0 +1,16 @@ +################################################# +# +# (C) 2018 Emanoil Kotsev +# deloptes (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# +#KDE_LANG = en +#KDE_DOCS = kdebluetooth + +tde_create_handbook( DESTINATION tdebluez ) + +#tde_create_handbook( LANG de DESTINATION tdebluez ) \ No newline at end of file diff --git a/doc/en/bemused.docbook b/doc/en/bemused.docbook new file mode 100644 index 0000000..0e968c9 --- /dev/null +++ b/doc/en/bemused.docbook @@ -0,0 +1,10 @@ + +Bemused Server: Control your media player with your phone + +The KDE documentation for this component is still being developed. Meanwhile, please visit the Bemused homepage for more information. + + +If you have any questions about it or if you want to complain about the missing documentation, send email to Alex Ibrado. + + + diff --git a/doc/en/components.docbook b/doc/en/components.docbook new file mode 100644 index 0000000..5df0061 --- /dev/null +++ b/doc/en/components.docbook @@ -0,0 +1,15 @@ + +Components +&components.tdebluez; +&components.tdeio_bluetooth; +&components.tdeio_obex; +&components.tdeioclient; + + + + +&components.handsfree; + + + + diff --git a/doc/en/concepts.docbook b/doc/en/concepts.docbook new file mode 100644 index 0000000..a0f2bf7 --- /dev/null +++ b/doc/en/concepts.docbook @@ -0,0 +1,85 @@ + +Concepts + + +Bluetooth security: Pairing devices + + +What is "Pairing"? + +Very often it is required for a device to authenticate iself when it +wants to access a service. In that case the two devices needs to be +paired. +When two devices are paired, they can be sure about the identity of the other +party. Without pairing, you would have to rely on the address or name of +the other device, which can be faked easily. + + +Pairing usually happens one time between two devices. After pairing, connections +between the two devices will be authenticated automatically. + + +Usually the pairing process will be started automatically when it is needed. +You do not have to worry about a device not being paired if you want to access its services. +If they try to authenticate, but fail, the pairing process will be started automatically. + + + + +How does it work? + + +Devices are paired to be sure about the identity of the other side. But the +first step can't be done automatically. You have to make +sure that you know who wants to pair with your device. This is done by entering +a "PIN" number in both devices. The notion "PIN" is widely used, but misleading. +It is not the type of PIN you have to enter to get money from a cash machine. +You don't have to remember it. And after (!) the pairing is done you don't have to +keep it secret. You only have to make sure that nobody else knows that number +until you (or you two) entered this number in each device. + + +To be on the safe side, you should not only keep the PIN secret during the pairing +process, but you should also use a random number, which can't be guessed easily. +TDE Bluetooth assists you here by creating a 8-digit random number itself if possible. +You can also use characters for a pin, but then you might have problems entering +it into the pin dialog on a mobile phone. + + + + +The PIN helper + +But where should the pin be entered? As it was noted before, the devices will simply +ask you for the PIN when is is needed. For BlueZ, things are a bit more complicated. +There are several ways for BlueZ to get the PIN number from the user. + + +TDE Bluetooth makes it simple by offering Authentication Agent that interacts with the +bluetooth subsystem and offers dialogs to confirm or fill in PIN. +Please see the setup instructions on how to set +up the pin helper and what to do if it doesn't work. + + + + +Managing paried devices + +After you have paired many devices you might ask yourself which devices are paired and which one not. You also may want to remove a pairing. + + +First, no device can ever know for sure with which devices it is paired. When two devices are paired, they share a secret link key, which was created during the paring process based on the pin number and some other ingredients. Because the other side may decide to delete a link key without notice, haveing a link key for a given device doesn't guarantee that the link key on the other side still exists. If one link key is gone, the pairing does not exist anymore. Of course you can be sure that you are not paired with a device if you don't have a link key for it. + + +So how can link keys be removed? That depends on the device. Most phones or PDAs have a list of "paired" or "trusted" devices, where you can remove single item from somehow. +In TDE Bluez you can remove the device by using the "Devices..." and then select and delete the device. + + +There is as special annoyance involved, when you frequently switch between different operating system which both use bluetooth (Linux<->Windows usually): When you pair your phone under Linux and then boot Windows, Windows will know nothing about the link keys managed by Bluez. So it appears as if the computer has dropped the link key and you will have to pair again. Depending on your device it might not even be possible to pair again without removing the "old" link key on the device before. + + + + + + + diff --git a/doc/en/credits.docbook b/doc/en/credits.docbook new file mode 100644 index 0000000..24e91a6 --- /dev/null +++ b/doc/en/credits.docbook @@ -0,0 +1,55 @@ + + +Credits and License + + +&The.framework; + + +Program copyright 2003,2004,2020 &The.framework; team + + +Team Members: + + + + +Fred Schättgen kde.sch@ttgen.net + + + + +kbluetoothd +tdeio_bluetooth +tdeio_sdp +kbtserialchat +kbthandsfree (disabled in 1.0) +kcm_kbluetoothd +bemused server +libkbluetooth +kioclient +kbtsearch + + + + + + + + +Documentation Copyright © 2004 Fred Schättgen kde.sch@ttgen.net + + +Documentation Copyright © 2020 &Emanoil.Kotsev; &Emanoil.Kotsev.mail; + + + + + +&underFDL; +&underGPL; + + diff --git a/doc/en/developers.docbook b/doc/en/developers.docbook new file mode 100644 index 0000000..31a5d65 --- /dev/null +++ b/doc/en/developers.docbook @@ -0,0 +1,25 @@ + +Developer Information + + +TDEBluetooth +This section describes how to utilize tdebluez for your own application or access it from scripts to retrieve cached device names or similar. + + + +The tdebluez library + +The library provides access to the implementation of the DBus interfaces specified in the kernel documentation. + + + + +The tdeobex library + +The library provides access to the implementation of the DBus interfaces specified in the kernel documentation the kernel documentation. + + + + + + diff --git a/doc/en/download.docbook b/doc/en/download.docbook new file mode 100644 index 0000000..719aff7 --- /dev/null +++ b/doc/en/download.docbook @@ -0,0 +1,16 @@ + +Getting &the.framework; + +Sources + +&The.framework; is part of TDE - the Trinity Desktop Environment. +You can download and compile the the sources yourself, but we recommend using a package for your distribution. + + + +Or you find the source code in the git repository. + + + + + diff --git a/doc/en/faq.docbook b/doc/en/faq.docbook new file mode 100644 index 0000000..e60263f --- /dev/null +++ b/doc/en/faq.docbook @@ -0,0 +1,21 @@ + +Questions and Answers + + + + +My problem isn't listed in this FAQ. Where else can I get help? + + +You can ask on +Gitea +where you can post your questions and comments or on +gmane.comp.desktop.trinity.devel. +Please look in the archive first before posting, but don't hesitate to ask +us if you didn't find anything helpful. + + + + + + diff --git a/doc/en/handsfree.docbook b/doc/en/handsfree.docbook new file mode 100644 index 0000000..0b6e48f --- /dev/null +++ b/doc/en/handsfree.docbook @@ -0,0 +1,15 @@ + +Dial and talk using your computer + +The handsfree client is an implementation of the Bluetooth handsfree profile. +This protocol is similar to the headset profile, but it provides more +functions than the older headset profile. Originally, the handsfree profile +was designed for use in car handsfree devices, but more and more common +Bluetooth headset support the profile today. + +Modern linux provide ofono as modem manager. For the moment it is possible to +connect the HFP and use external ofono frontends for managing calls. + + + + diff --git a/doc/en/index.docbook b/doc/en/index.docbook new file mode 100644 index 0000000..de24b01 --- /dev/null +++ b/doc/en/index.docbook @@ -0,0 +1,164 @@ + +The TDE Bluetooth framework"> + the TDE Bluetooth framework"> + TDE"> + + + FredSchättgen"> + kdebluetooth@0xF.de"> + EmanoilKotsev"> + deloptes@gmail.com"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]> + + + + + + + +&The.framework; Handbook + + + + +&Fred.Schaettgen; + +
kde.sch@ttgen.net
+
+
+ + +&Emanoil.Kotsev; + +
&Emanoil.Kotsev.mail;
+
+
+
+ + + + +2004 +&Fred.Schaettgen; + + + +2020 +&Emanoil.Kotsev; + + + +&FDLNotice; + + + +2020-07-27 +1.0 + + + + + +This is an overview over the TDE Bluetooth Framework. The framework +is a package of several tools and infrastruture for TDE, which are +usually used to communicate with bluetooth devices such as mobile phones +or PDAs. It is built on top of BlueZ 5, the official Bluetooth stack for Linux. + + + + + + +TDE +tdenetwork +tdebluez +Bluetooth +Rfcomm +BlueZ +OBEX +Sync + + +
+ + + +&introduction; + +&news; + +&download; + +&installation; + +&components; + +&concepts; + +&faq; + +&developers; + +&links; + +&credits; + +&documentation.index; + +
diff --git a/doc/en/installation.docbook b/doc/en/installation.docbook new file mode 100644 index 0000000..4df17a1 --- /dev/null +++ b/doc/en/installation.docbook @@ -0,0 +1,77 @@ + +Installation + + +Requirements + +Dependencies: + + + +TDE >=14.0.9 + + +Openobex >=1.5 + + +TQt >=14.0.9 + + +bluez5 >=5.14 + + + + + + + +Compilation and Installation + +Create build directory and "cd build". + + +Run cmake .. + + +make install + + + + + +Setup + +The TDE Bluetooth Framework itself does not need any setup. + + + +Use hciconfig and sdptool to check low level if needed: + + +root:~# hciconfig <- Check if adaptor was found and is up +hci0: Type: USB + BD Address: 00:10:60:A3:8B:DE ACL MTU: 192:8 SCO MTU: 64:8 + UP RUNNING PSCAN ISCAN AUTH ENCRYPT + RX bytes:176023 acl:2046 sco:0 events:1809 errors:0 + TX bytes:23000 acl:971 sco:0 commands:469 errors:0 + +root:~# hcitool inq <- Try to seach for other discoverable devices +Intquiring ... + 00:A0:32:1A:B0:27 clock offset: 0x122b class: 0x000100 + +root:~# sdptool browse FF:FF:FF:00:00:00 +Browsing FF:FF:FF:00:00:00 ... +Service Name: SDP Server +Service Description: Bluetooth service discovery server +Service Provider: BlueZ +Service RecHandle: 0x0 +... +root:~# + + +The first command checks if your bluetooth device is up, the second searches for +other devices in range (they must be set to be discoverable). The last command lists the services that are offered by you to other bluetooth devices. If you have tdebluez running already, you will get the full list of active services here, including obex push etc. If that succeeds you should be able to use tdebluez without any further setup. + + + + diff --git a/doc/en/introduction.docbook b/doc/en/introduction.docbook new file mode 100644 index 0000000..04aa88d --- /dev/null +++ b/doc/en/introduction.docbook @@ -0,0 +1,24 @@ + +Introduction + + + + +The TDE Bluetooth Framework is a set of tools built on top of Linux' +Bluetooth stack BlueZ. Our goal +is to provide easy access to the most common Bluetooth profiles and to make +data exchange with Bluetooth enabled phones and PDAs as straightforward as +possible. + + +The next chapters will explain how to set up tdebluez as a whole; the +following chapters deal with each of the tools, how to use them and how +to set up each of them. + + +Have fun :] + + diff --git a/doc/en/irmcsynckonnector.docbook b/doc/en/irmcsynckonnector.docbook new file mode 100644 index 0000000..c295b70 --- /dev/null +++ b/doc/en/irmcsynckonnector.docbook @@ -0,0 +1,109 @@ + +IrMCSync Konnector for Kitchensync + +This program is a Konnector plugin for kitchensync, it let you sync your device supporting IrMCSync with your local Calendar and/or AddressBook. + + +By now Calendar and AddressBook are supported! + + +I SUGGEST YOU TO MAKE A COPY OF YOUR CALENDAR AND ADDRESSBOOK BEFORE TRYING ANY SYNC. +I'm not responsible if your data will be corrupted or destroyed. + + + + +We need debug infos! + +PLEASE run kitchensync from a shell or redirect stdout and stderr to a file so if you have problems you can send me the konnector output.To enable the debug features you can launch "kdebugdialog" and set on the kitchensync related options (from 5200) and the generic option. + + + + +Compilation. + +The actuall kitchensync from tdepim SVN has more features and work better than the tdepim 3.3.0 version. +I'll suggest to use this version. You have to install at least kitchensync (and all his subprojects) from SVN. + + +There's also a tdepim-3.3.0 version that you can find in the irmcsync_tdepim_3_3_branch but it's not mantained anymore. I'll concentrate my work on the SVN one. +This version depends on tdepim-3.3.0 and the older or younger versions, so you have to get and install it. +If you want to fetch the irmcsync_tdepim_3_3_branch you have to move to +kdebluetooth/irmcsync/irmcsynckonnector and type: + +cvs -f -z3 -q update -d -P -r irmcsync_tdepim_3_3_branch + + + +To enable the compilation of the konnector you have to add the "irmcsync" dir in the SUBDIR variabile of kdebluetooth/Makefile.am + + + + +Using KitchenSync. + +In these days (25/03/2004) KitchenSync is very experimental so you can get various problems. + + +After having launched it, you have to choose the konnectors to use. +you need at least 2 konnector to make a sync, but you can use only one for tests pourpose. + + +To add a konnector go in Settings->Configure Kitchensync + + +If you get some errors and the widget is now well painted try resizing the configure window. Then in the Resources Combobox you'll get various chooses, ALL ARE UNUSEFUL AND DISABLED, only "konnector" is the right. + + +For Example you can add the IrMCSync Konnector and a Local Konnector. + + +In the IrMCSync Konnector select your device, and check the Sync Calendar CheckButton or the AddressBook (for now you can't sync both at the same time). In the Local Konnector Select a Calendar File AND a KAddressBook file (you have to choose both and with right filenames or it will not work). I suggest you to create a temp directory in which you put a copy of you original AB and Calendar so you can make some tests. + + +You can find them usually in these dirs: + + +CALENDAR: $HOME/.kde/share/apps/korganizer/std.ics + + +ADDRESSBOOK: $HOME/.kde/share/apps/tdeabc/std.vcf + + +Then you have to create a profile in Settings->Configure Profiles and for every profile you can add various parts, every part do a different function: + + + + +Synchronize Part : Make the REAL Sync. + + + + +Konnector Debugger: Let you Debug a Konnector. + + + + +Konnector Backup : Make the backup of the data. + + + + +Overview Part : Like the name says. + + + + + +If you want to sync, you have to choose at least the "Synchronize Part". + + +Then, to make the sync, click on the top-left button. + + +You can check the konnector, creating a profile with the Konnector Debugger Part and pressing the Connect Device, then the ReadSyncees button. + + + + diff --git a/doc/en/kbluelock.docbook b/doc/en/kbluelock.docbook new file mode 100644 index 0000000..9150a51 --- /dev/null +++ b/doc/en/kbluelock.docbook @@ -0,0 +1,10 @@ + +Automatic screen unlocker + +There is no documentation for this component. + + +If you have any questions about it or if you want to complain about the missing documentation, send a mail to Mattia Merzi. + + + diff --git a/doc/en/kbtobexsrv.docbook b/doc/en/kbtobexsrv.docbook new file mode 100644 index 0000000..629c7f5 --- /dev/null +++ b/doc/en/kbtobexsrv.docbook @@ -0,0 +1,49 @@ + +OBEX Push Server: Receive files + +Most Bluetooth-enabled cell phones or PDAs will let you send +files from the mobile to your computer. While it might be more +comfortable to use the OBEX FTP tdeioslave +to pull the files from your phone to your computer, not all devices +support this. OBEX Object push on the other hand is supported by most +Bluetooth devices. + + + +Using the OBEX Push server + +The OBEX Push server is managed by kbluetoothd and will be automatically +started if it is enabled and someone tries to send you some files. +Please see the documentation of kbluetoothd for how to +enable or disable +services (The OBEX Push server is listed as kbtobexsrv). + + +When you send a file from your mobile to your computer, the kbluetoothd +tray icon will turn blue and shows a small popup. By default (depending +on your +settings for incoming connections) you will have to accept the +connection first, then the main OBEX Push window should pop up +and start receiving the files. + + +Incoming files are saved to /tmp and will be deleted +after you closed the OBEX Push server. To save to files, you have two options: + + + +Click OK to save all received files to +a predefined location. + + + +Drag one or several files to some other location. This is especially useful +if you want to send files by email, since you can drag the files +directly into kmail without saving them to temporary location first. + + + + + + + diff --git a/doc/en/kbtserialchat.docbook b/doc/en/kbtserialchat.docbook new file mode 100644 index 0000000..9f1c4a6 --- /dev/null +++ b/doc/en/kbtserialchat.docbook @@ -0,0 +1,44 @@ + +kbtserialchat: A very simple chat tool + +The kbtserialchat tool is a very simple chat client&server. +Its primary purpose is for debugging and to serve as an example on how to +write a bluetooth server that is managed by kbluetoothd +and how to write a client that can be started with the +SDP tdeioslave. + + + +Using the chat tool + +kbtserialchat implements the Serial Port protocol, +so when you start it, it will show you all devices which +provide the Serial Port profile. This will include other computers +where the KDE Bluetooth Framework is installed, but also +phones which support this profile. + + +When you are connecting to another kbtserialchat server, then +kbtserialchat will pop up on the other system and you +can happily send each other stupid text messages. +You can also connect to the serial port of Bluetooth phones +and send arbitrary AT-commands. To dial a number for instance, type +ATD<number>;. + + +While it may not make much sense to connect kbtserialchat to +a serial port of a device that uses it to exchange binary +data, you can use it not only to connect to serial port services, but +to every service that's based on Rfcomm, like the handsfree or headset +profile. Since these services are not listed in the dialog on startup, +you can use the following trick: +Browse the services of a device with tdeio_sdp (using a sdp:/-URL). +Then instead of simply clicking on the service, right-click on it and +then select Open with.... +Enter kbtserialchat in the dialog, press enter - and +you're connected. + + + + + diff --git a/doc/en/khciconfig.docbook b/doc/en/khciconfig.docbook new file mode 100644 index 0000000..7956335 --- /dev/null +++ b/doc/en/khciconfig.docbook @@ -0,0 +1,10 @@ + +khciconfig: Explore your Bluetooth configuration + +There is no documentation for this component. + + +If you have any questions about it or if you want to complain about the missing documentation, send a mail to Mattia Merzi. + + + diff --git a/doc/en/links.docbook b/doc/en/links.docbook new file mode 100644 index 0000000..f1f0eb5 --- /dev/null +++ b/doc/en/links.docbook @@ -0,0 +1,27 @@ + +Links + + + +Other projects and documentation + + +BlueZ +This is the official Bluetooth Stack for Linux +and the base for tdebluez. + + + + +Linux BlueZ API +This is the official Linux API and the base for the dbus implementation. + + The implementation is done by using dbusxml2qt3 to generate the interfaces. + + + + + + + + diff --git a/doc/en/news.docbook b/doc/en/news.docbook new file mode 100644 index 0000000..f5b3351 --- /dev/null +++ b/doc/en/news.docbook @@ -0,0 +1,4 @@ + +News + + \ No newline at end of file diff --git a/doc/en/othertools.docbook b/doc/en/othertools.docbook new file mode 100644 index 0000000..526c3eb --- /dev/null +++ b/doc/en/othertools.docbook @@ -0,0 +1,31 @@ + +Additional Integration- and Command Line Tools + +This sections lists all the other smaller tools and script that come +with tdebluez. Most of them are of limited use alone, but supposed +to be called from within scripts or by other programs. + + + +tdeioclient: Access to the TDEIO OBEX framework + +This is a little utility to access tdeioslaves from the command line. +So tdeioclient is actually a general purpose utility. + + +With tdeioclient you can copy, list, delete any +file or directory which is accessible by TDE's TDEIO Framework. You can +copy a file from a FTP server directly to another server using WebDAV +or list a directory via SSH with tdeio_fish. Or - and that's where it +becomes interesting for us - you can access files on your mobile phone +via tdeio_obex. + + +Please note that tdeioclient - even though it is a command line program - still needs to be + run from within KDE, so you won't be able to use it in cronjobs for instance. + + + + + + diff --git a/doc/en/tdebluez.docbook b/doc/en/tdebluez.docbook new file mode 100644 index 0000000..fcb849c --- /dev/null +++ b/doc/en/tdebluez.docbook @@ -0,0 +1,53 @@ + +tdebluez: The Core of the TDE Bluetooth Framework + +TDEBluetooth is a central piece of the TDE Bluetooth framework. It runs all the time in the system tray usually and starts other services like the OBEX Server or +the authentication agent. Its system tray icon tells you about ongoing bluetooth connections and +also gives you quick access to the service configuration, recently used services, devices in range +and more. + + +Programmers who want to integrate their program with tdebluez, should take a look at the developers section for more information. + + + +Starting tdebluez + +You can start TDEBluetooth by typing tdebluez on +the command line or by starting it from the TDE-Menu (usually System/tdebluez). + + + +TDEBluetooth displays and icon in the system tray. + + +When tdebluez is running until the system is shut down, it will be started again automatically next time you log in. To keep tdebluez from starting up automatically, uncheck the "Auto Start" option in the configuration. + + + + + +What tdebluez does + +The goal of tdebluez is to provide user interface and interoperability to the Trinity Desktop Envitonment. The user is able to perform bluetooth related operations such as power on/off, discover, pair and connect varios bluetooth devices. + + + + +Configuring tdebluez + +tdebluez writes its configuration in ~/.trinity/share/config/tdebluezrc +Configuration-> + + + +Local Services + +Services can be enabled and disabled in Configuration-> + + + + + + + diff --git a/doc/en/tdeio_bluetooth.docbook b/doc/en/tdeio_bluetooth.docbook new file mode 100644 index 0000000..0a3d7cf --- /dev/null +++ b/doc/en/tdeio_bluetooth.docbook @@ -0,0 +1,41 @@ + +Bluetooth-tdeioslaves: Devices and services + +The bluetooth-tdeioslaves let you handle bluetooth devices and +list and use their services with konqueror (or any other file manager that can +use TDE's File IO framework). + + +Using the Bluetooth tdeioslves +To search for devices, simply enter +bluetooth:/ with konqueror. You will immediately +see an entry for your own system. + + +You can also click the Bluetooth-Icon in the konqueror sidebar in +the "Services"-tab, next to the LAN- and Audio-CD-Browser. +If you can't see the icon it might help to reset the sidebar +with config-button -> Add new -> Rollback to system default + + +The devices must have been detected and paired, so that the slaves can communicate with them. + + +When you select a device you will get a listing of the services +published by the device. If the service is supported by tdebluez, +you can click it to launch the appropriate application. +Unless you've disabled it, konqueror will pop up the common file-open-dialog. +In case the service is not known, you will get an open-with-dialog instead. + + + + +Current limitations +tdeio_bluetooth has no support for nested browse groups currently. This won't +be hard to add, but wasn't need so far. + +You can not connect to services of your own system. + + + + diff --git a/doc/en/tdeio_obex.docbook b/doc/en/tdeio_obex.docbook new file mode 100644 index 0000000..c58a299 --- /dev/null +++ b/doc/en/tdeio_obex.docbook @@ -0,0 +1,61 @@ + + OBEX-tdeioslave: Browse folders over Bluetooth + + + General + The OBEX protocol is designed for use with mobile devices. If you have "beamed" already a vcard from a mobile device to an other mobile device, you have used OBEX. But there are also other applications for the OBEX protocol. Most notably the filesystem browsing protocol. If the mobile device understands this protocol, you can browse, up and download files to your mobiles filesystem storage using this client implementations. Also syncronisation protocols like IrMCSync or SyncML have OBEX bindings and can be accessed using this client, even if there is no usage for syncronisation in konquerror. + OBEX protocols can use many different transports. The original transport was IrDA, but there also exsist transport bindings for Bluetooth, serial lines and tcp/ip connections. + OBEX supports 2 way authentication. The first, most known way, is authentications of the client by the server. So the server implementation can verify the clients identity. But also the client can verify the servers identity. For authentication a MD5 checksum challenge is used. This enshures that plain passwords are never sent over the transport medium. + + + + URL format + OBEX resources are accessed using URLs. The protocol part is clearly obex: The path component holds the path on the server. The host part is a bit mor complex. + For servers accessible over tcp/ip the host part is as usual. You can use the hostname or ip address of the server host to contact. Also If the server runs on a non standard port (the standard port is 650) you can append the port number as usual. + Example: + OBEX://hostname:port/path + For IrDA or Bluetooth transports you can use the hardware address in the standard notation with octets separated by double colons. Neat, but a bit difficult to remember the hardware address of your Bluetooth device. + Example: + obex://[ef:01:23:45]/path + or + obex://[12:34:ef:01:23:45]/path + Therfore it is possible to define hostaliases for use with the OBEX protocol. These aliases are defined in the OBEX KControl module. You can set up a human readable name, discover devices and finally assign a hardware address to that name. Also Devices ober serial transports are accessible via those aliases. For IrDA and Bluetooth there are handy predefined aliases named irda or bluetooth. Both do device discovery and try to connect to the first device it finds. + Good luck browsing your neighbor's mobile :)) + + + + Tips & Tricks + + Like every other tdeioslave, you can directly open and save files to bluetooth + devices with tdeio_obex. So if you write a shopping list for instance, you can + simply type it with kedit and save it on your phone. + + + You can make this procedure must faster by adding a bookmark to the + bookmark list of the file-save-dialog. + + + + + Limitations + + + OBEX and TDE + Since a tdeioclient can't control the number of slave which are accessing the same destination it is often the case that there are multiple slaves running. But OBEX transports, except the tcp/ip transport, support only one transport connection to the device. This leads to tdeioslaves which fail to connect with "Device or resource Busy". OBEX has a partial solution for that problem. If the server supports this, one can transmit packets for multiple connections on one transport connection. But, I have not seen a device which announced this feature. And this would require a separate transport daemon for each destination. So, if I see devices providing this feature, I will start to implement that daemon. + There is no special method to rename or move a file on the device. So this is done by copying the data from and to the device. This is slow and will start two tdeioslaves which will have the problem described above. + + + + Device compatibility + Since this client implements an open standard There is a real hope that there are much devices out there that will work well. But there are always exceptions. + + + + diff --git a/doc/en/tdeioclient.docbook b/doc/en/tdeioclient.docbook new file mode 100644 index 0000000..0645d80 --- /dev/null +++ b/doc/en/tdeioclient.docbook @@ -0,0 +1,8 @@ + +OBEX Push Client + + What is this? + tdeioclient is a program that let you send files from your PC to any bluetooth device that support the OBEX (Object Exchange) Protocol. This Protocol is supported (probably) by all the older bluetooth cellular phones, palms and of course another PC with a bluetooth device (and possibly tdebluez installed!!!). + + + diff --git a/doc/images/adapterconfig.png b/doc/images/adapterconfig.png new file mode 100644 index 0000000000000000000000000000000000000000..4691d23d465d962739fe71c44ec0a7d8bc328802 GIT binary patch literal 42657 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 O0000 literal 0 HcmV?d00001 diff --git a/doc/images/devices_discovering.png b/doc/images/devices_discovering.png new file mode 100644 index 0000000000000000000000000000000000000000..c1a214fd7b7bb2fe9338e5eaf478c6acaa075811 GIT binary patch literal 59132 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%0AsF}+5DxYPhNQMUOmW|p|u@rjv&iM%h#(LLU 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

R^M}U1wUZ?YO6Ptba929ddz#4TEn7a=5n9N zj^CXW{feYLt52d&@AqC&)0O?7?+v^ZFL#U->c59r=6rpU$)VR*BC~$PNj#uVAtm6n zVWz~3bKJoc`*vfbOR*JSrrM*UdvIoFb4r?1%CyMcfB9Suy4V zb!ENmCz+gj>AS~P2VS9dVNwk)1K%7J3^a->?~n0@1~03s3u!!SboiQqj;qPrJ~8Gm zz9Ek>QcJ#b4iz$CwudY8I5vM^u0IPS&QTqovj!DjK-*Lzb&2Y1s^@ zyKBaBwn*ItzQg9`%2`JT>V4_(Xa-jFQJF>Iu#Jv9je* zkMQYoLn7+;T00?Gc_Q*AMHNsYb6*X;Q;U+zbS)#$&sDncP{Qw5yAW}oE{s@wFmYcy z>EhWc{)@r>y&?-Hg4;^v83hWtcsjfeN7R@>#xu(;m1V{6-t^pAlHHi%T5=@fN1guJ zxT|^)zLz*$&fn+R{#7;;W9!g?h{EP@w zLWPsmQQd+sy*8P#xFc>aanr>i%4*exLmkOb_)A*%oS4~Ps;KKoKZ!i=wU|q0PE7FL z&1=MaUm8y1?4>>N4UD=Rj+Ct#sFl?$t;+eVt?94~TxtR}Hsf!7)VR7K9Y;dxzc0~x zg?b0c)h>6V^(-xyiOjB|cNotnJf!8|xvyZqz0cVY?QmuX>9Cpb5-zEP3Y9WFXLF=- z6Do17(86g4EI&4R{I|;AQ=<0QRD|z~`6zf3LhY+j{|4DAcQqJxzLU~-r2%Q@oh}#k zpU$3Yo~CMcZZZ8c>pU3b{HKzh(XE}mF*B%P(F}v-VkQr9rI?t44S9TKcGqpK$}8eM zS9kad;%9x>iz1j@_uu>RO~~4_Vua)}s;qes{G@(0a}`V?J3m%2C@)zS9LAz6m@7!9!UoqcO`JS6RdN>`#4#cYf8NHCX1wlG|J+vDTGlUg zsuQ8;KM+*7OeR{WXUbwb}k`0$wS96on#zZv+C1pFlH^Y%z3L|ewW%>_MOx>QzD7< zeqP)L(4--x-0NS?HuQfsBn98b>CYm3`PL*UYk^%!kUV7BOv%$yJUSY)&!xd)-zEJq z`7-T{Q`}BxLSEwin1U-LSaCG(DK_<(&(_|B z0kbrj;0*%Y0;$<`lP6;ScRtt(y2zNU5sVbir6fuCugkt_e2fv)G-HCVHf*(jmBRc7 zM}6Cl6yvr!beSJ;{wlimZ3WG)5Ek z5AA#=6He`Uy9HYwxE8r?ZK}TaZsWN7?wuw(E?v6*@I^}#me7G8dEREZ?X<^!Jh%S& zcw|H+1w-MWlpu1at6aK)FyIESt+&60o06IiU3PF^$JY-!`PKIvV>rkzB4Ts&H{~%z z@uNB0`pz0Ft(#`hf{)ksYOB>NS3IW5UL@n)bSFK3sqk&diHI-Bb#1xdIL7XUbOV3f z!`?*)d2%LUkGaX%akp)EtZ+PFJsxU`a9S`qStpLUvuDCI|UL*~R>X z$K0qm=?0zmup$_4bLck4Cvw_IeXM`qH-pL}aHZ*-ft@a>V$PYEnuK)9)W(C*?e{iA zr+mYmHO!j^+@}F}`mDF|gRb+JHcEt_X;6Qp$MlWT=HY5yXhOZlUJ&ioX=`Gn-(^>L zMIe1rzRcugah_F+1m)k4i&Y zxbN}a0sd3=Ro33xqwDX#EBX3P zlIP2As8R?f?H>0U@QBD_8GPM)S|2~#iYBaIP_OraoxM5NmsIlIb9YI#B)^B7U3Cq5 zT@A)7V{d01dh+Hgi7Us)mh1HL&^uKVYQyB|7-<6E)T6l$5-gucC3=Gvd)bIh_pHJs!Fj9QDswwS0}l^0JUW$`=QGbP+EP)^e@tFqwqxgblDL7>qhV29lRY}RnW>O=jdb14 zXt}S!(RV?8p<$rJG+r{SukSWa(~MJq(dY7ybQOysG)QNegq@cO{5P~+tMoP6W_E5U zdw=eZ@2dR$hDOZf$IxU4Uv_RjfhMmePZ`dcXH$GWqnnNO%A~D}SEURrx`y`bZ=8Oi4q{?#D03Ba!++1?S9fpN6F;_%uhVVv|q%pZ(C`I}Jrn zzzyLH<;YlfI$1MpMm}jPEt(C<&y}@v5Xa5-maHEk3BvFg%aScXsW@OcD`M8IrE#;^ z%^G6k9W#kGqwu`tuPA~XjA^ROI=qert1XV(n^H;enyVK>@l#pnizMR$Fr^`=*$4q?^@uv2$e6h>kM9 z`sEBNQJ^SB6Wsp}6n?M`BiT%S-^Y$D$Z=5f$cV5cpDi@cpavTfVFfYVb`FU7`W{%t zq((s7g7DGl@CAEPfarKkUhd=7XJ)nSy=kMJ0>xo;G3BX82KZvi2kqX43>`D5Oc(Wm zBBZemN$s*E$BKtX%HCLtKi;U#qTM%=l(rs=4>msu%97)fWIVJs!MuOqBh8_6W$TVB z&*Y|IY4&EM-~^xi>+2LxJs(gHFMc@lYyUK?)#*ztKzn`b=(aYmiGKCODn&9nAq#3g z8Bw6;;OZma`!AXXM5*0&VE?-}V85tCY{fm}cVf}49jqRO{a^7tO+9lZ@-A621(vM2 z|D&tl=Hl_xkxlm$HIrIJU)FVIsYM<|)@;$XzOrPS-Cy&80v0yV+}X1(9s^N<)ur z@p}CObOb_PL<-QT2oTWQxCq2^K#_>o04wj(z%Nw5O>dzh5FTS9)QpUB2Xs0*I+Bu- zl$0skb8SnDi(LGh-(FlZWgqq4taDEpKp?&uJKQ&9C0bow?dvsrTK$T&Wys;4LDe{rUe#MiOlgpD0hR8%xSZ{k+qIM?dB zKIYo;1|M0779c_!<$JskdkqE4#o0NL!(5@s@??LFE~bM&jDaV)dmywmaXE7yJw3|zZ<)r4J3SsC}>0pgn|I%{`H#j+aUfw|W zJKJs(@;%Yi)I2*`D>QC@t;1guSPu`Woyt&#KKfvD`Z6s-$jF{1Proi981CqBecXL#Ub=#=vs-X|Ys-P3pMN)W zd9tQ5Am9eh!#ikhQl_R00qBGuWbWh0rwAo=E#NB2$zi~*`aKiW410vzX5`z9!}Y5# zo<4X)UhaLk(Gp5%P~#RJY-3|HlKg-}1J$XjcC?!tFEqQGj4U+m^k*oJWbIrq)YXm z{e189)1&Fji$i$&u-=9!Gsz=!Msd?MHW>QGlpOFBgQK!$u* zUtb%}?SvNZW$U{Hp@hu3?a9gr}DCV9G5><=HVK#Xv4aVd7#jg**nEp)}|a38!_ z8^~0A{PgUo zv-tT%Izto}4=+;{Kly$OliW^j-kNDTJy@@?9j{18AUQz0$HrD{(BM~4P~hk1$6?m};Ne3{ zp5Y)$zvbV*pX%z`kd>JCQN+u+t&ef;im&Rel#ZAGY1C;GU@Nn z_e=YPoLyaqEFO`Bn{CgwG&VNE)x&uW4GkexwZ8oTd7&?nyRWZL5pr`tK=CB|J&CYC zYtoa*4TWa0CvlCba%O~p>sC$k;gmNEAK%!(Kq)LYl#{NmE)ELL?!3(6Cc3+KpY^AT z7+yn2KCWiM3 zOw1Y{jfd@HEr)L;NKDhobBfg6&r!`;8OVg7KTmOdNY3Z-d)ouDJI}4}bA-6awvvnt zNWC0lWezjnmzS3z$JmdRS#C^J37>6=YHG%9<5@p)Luw`|R2sKo%|+(xR30El2in>) z7sR`7p`**n%bQ|js;Q}EDkS|}ammikMvkQTova)$r4XV8aG3Y%mYVfI7{kxp?5vEe zY};G%T=m@14)uKPboJcBjY;q2ADO34~x=7%MOB#H7{AV27_+ywCoHw`6l<5 z#}cf1sdaVdXUDrWu4`txolxY6c8-q52cw~*+fP=PoAo3N8~FQ< zmY5>5Kt2Qn1{N4J)K7)ibJ>o)e*M~Jy59F>Z$(V?=~D;fNO7OgX;`qfezT7h+~wYU zN7VVr{wd^K4}s*^*kLF@BBG*d^lGqNkW#I!t^a(&v&NT@1xuYnT4q6xzzr9e_a%$X z&&(8i?OVAGCfe8YS`B7FU>`2`H+6Qt>03HIIpNYR2n`Kg8S$PZ067C;x#b}cFIVAr zeo~-UV>emtdUmwEI6UwnDG7dGJM!^sn!SJjJ}I}|N4W0O%?3p=P&s*dc@y=%{5aXF zIS@qHM67p(gp!kZSXo&G1bjB9>fXJ3CnO}q@4gWh9DFww4*rs!p08I^F@x%=tE+2b zLQ{hm5^NZ^zc$PeEfZ+ieaF5GhlHyJih>H$=zCv1dz_vPLXIAb&NH3%y`*RL0X-~^ zu#ix^Hfwi#o%;lXUfmUl(ZH2}oyeDDH{brQl?qbm;r2G$L3kVqx1Fw;+2Zl;Qiuhl z7V3A)(HiK{LzzIa?@ zKV1*&8ygdok)Cb~k88L<54qi|*{m5^XQ-*Bw%^Bj3uk|GI;CxnI3hYZIa&Df%(-;U zGDkEBOP+jeB-(Q&JvK}(Dcp2zh_jSXVVdUb;Sy1i zJzLE!G&274qvIEN{Z@#if}I^RPE~dFRF!kvQ6m@JU`|$+1M5K$NZ?&C$}`rX%8@w7#b zY}G?)A< z$}lcNJuo~dHI(!qc34<`DBsc1`zx6# zT#UWy515!Zv6SeeS5{V_C@qExN5V}XxCdO*uIvC%~H;WMB=@_8emK* z(5itu?R%&vBhwkjX8c-YhNDlQX~C)Cc#(8&BrGfpi9}|cC_~QG(bPO@M4>!7Ik|~~ zQhKe2u(+(ughe-O$x%LE6LxTb$V?lhzm2IW{+&A}=H`S%L|?u3SHVeu&Zg$`J#mqj z??H~1;-lU0PVY+&U)}fGP3&H9>d{q>ds{brlhH0n8SWM-zhN*_*z^)NH+ z(EIlnny`f*VLdXS5fC_ofH>M}!UhHG1VMpFM6Wd_P3$ zt%#`%#)p`g*LJMz{rmUgp#-kT5y$3WoZ&(P`$;b@0pGVFAzCFSA&>7aPd8k$G@O;V zuBp#uX_cBW=~b)d4h04U<*MdDdY)fgBxyLs#lc}S@BIjM1MF9c-s62uZS7VNQLU{q z=|6w`_yIcrtn9mck|ki3Ln~h&yNj2eo+Rg#e>|>yTIVjpKtd%fEKEyl2Bxna)x$>C({dO07pfWrfnEUYI!(ffO{ei~8H$#E)z5RW#;g|DK ziq!Y+<>%+)qqQ`?LV;(eqN0L=O<|LP(;7kP4`qF1L^EG~oRwxZye{)5v5?PKIod=% z*PO?9i6Hf$Q2Klmq8O;&w^{Dto;FEIgxHM|`K$@a>1cayRLQ<>|BsUh#3Tg|MEg#v z+I_R$Zjx^fC#FuT&iB-9AhB*AmTP2etN@G;T&h!*0LZzc9+uq}opvADgz4c!?fii5 z%>=K_*Xoa6%A}uwPu@;)v)~O39(&#EVc7jKx23#1=jW#Rk?=<19TJk&+1V1y0a~P9 zfJi@N3$jAi-ck-cA43UQj3N^F+&9Qbt{^tne!QXmSBT{Q>NEZ?2VplZ_otcqmz0;gy18vk z)t$oXep1ffC@uMm-53*{e!QEz9h^r4K*uBE=mhiy_-$N(5m|Y8-RpQ+mF~vorm+1~ZD*$pb%LOG zMP_EEL>gN!5hbO)wRK`_tR$wFQ-#eaEaWT_Nk&9;1kgrZLxWV3Q@fTbKx7-Zh`6Mr zx0hFi<$xJjbL7ilskDAZ{aVNdBOM(bLqnX5jEwj1xA4R|a$dc9m4He64h{}gY!{e( zP5V69U9}$DwVu0YXJ_-!B-7NCkhsa;`HVGBvp5yF2)MS-pFe}qj_oi36@6`L0q|?qk&&$K8xtVWut>S&G6%v*IAyX`Y46{U*MA8y z{k5uUD7V7rc!!w7%w^>#q)9_K6crWKzIOX2e@^ync)<{%fluSoZeOzC>29AeAt7PI z(QK$yc|x#R@5d^TnxGJAX=z(qTB2iOfN{l;4;8(9^{VM-x-7tlF$ZLhTJE6L{#2be zHaVZFipp-v}W$W2LazW zRYlC>@JYWAOr+q{C}NA|wV0LF>S(F?8s36ghoe4LJs zPErB{8Ltz_Oh~DnomSu;jEdthF;qYrs^t!e24b*b#j_fIe~pS4{D7BtRBk(vVW7d^ zpCZCs;1le!i?iLnC^|*h$&@(o5EcOwnwwMdxddZo2IBL9aZweytps&%yF4xJ9u9b^ zBpyeR+@XYmzF?ets{vw77S~x>9z0l%H&1@T|4kGNm5WN70#yQxx7hsZs@GgACUpWt z(uapCa&!+KJa~p33v&4MaFY^fn~{-`nAmGg#sZBTh)|F2{H25SakDqv^S^&TJIR%! zt#Mw$!Xf8(v$;4sj*pM`5t<$z9&T!S6^wCHR7FEd9w$Vsb(jn*i0P-pdD6#^+T_)U z3_1cR6zx>~)vxcSe)&QZBK9*|C39!TiCVkfr)ouu3r9L#ncB(O866dMw9eZd)JJJa ziO~7pKwx@ae*Wly&)n^F<@n@e3No_pw^lhGe=DEv!Ftov=YaBr_yL(Mn9?WgbHwV+ z)_=Q+^>FMC>>Xrk_!Sjh-LOi|iA?xlFee05SN3?OzFt6>D=RC5ce{57?bWMSplQ-& zWBD#SCMSIxU)}&wg=|(*QUZ$)0R(m*N`%#3$=LVrMwb`otyHi}NcdbVJUu;2Th{&} zXz*74DrkiNRsxMjyX-A1QU6@q$Ns_z!6Z)I4|_gbRL%fMHz+bDB^8w>{bLf0o6$1S z;CMcMoG3PKgG!nhY{EX8r&Sumtd}!n)zs9aMnCihp8?h?+~6~?kT?Lu;5NaJk1{8C zF)}e3!}BvXPFJ?Hu&{7*bF;BQw!gbKsz9~dvi6Xf`E6JjcrR^f>5f$KP%tb!M$N%% z!-cyZwb2Y3XxFb|1p!D%#w6vst(2z7UH&pH&Dh0d4@z<`Yd3_S_aB&AMKY-gQ$tKN zkOcTQYn$pTii)-l4*X8nDDcq~^V@^g9KT{=R8j33o1} zXCYvS83Z_Zc*fS&e~cnPlCFgvfC+JNaWQua&5yQbKrXBfW?y~rZ0mO;rO(zCZ#-%t zp^GR*A>hen6&30a?m)!{y(%X!|Hn^UvrGk#h92-8fDkNKhHNZziqB35ggX-x)Ar)c z=cjGAl@fmtvxLPvmw&BLS35b!xb(Ljg?!;_w2@7J=S{uuX@Pe6s)4?pn%YpZpf~vO zwyBSIQ=yaq8W!Z>7~r}7`{AX>_d1f=)i;l=3Oj3_CV0LMTJ*@%($w_X>z4!^8OQ6K z0d5yOf$;HsRIGs?G3pfr_H~ij?N$(apxz-rdhRY}tI}&|3`0>nKWw-Z?}9$YERGWp zl%R48fB#Fkg=d67VV!9IAJ^<}#v5!$yXlzxIVQ#b6<}2KA(y{i9heo@PZ&h*PaR_6j64C&*#qXm?KQSK zuNrStQZ~R5ZbuMobaHXOCo7ov<Tqa+AbF$_T92~SUE|2^Cp?MZ; zG4nkDS`DCfrphb_-~;@RW`c@}Sksk>;jttnbhbW*aD}Aq!l!3tMX$4-nd0Ylal8~D zLM6nDShn*zg0jRQQM_~n#Sgiblatfk-5nuxB1QeK_6(*_%F8S5Cbf&6H3B9ZMQt|P z{^~dfL~|Gd!OIn#(64-*wxwNCW++=GfNBAWdtaU(hFJXm_5y&)c%?lZb+k-5V1O&v zZZ@~ImExh?ya|2_ZiE}2Ok0~dlzyPOVlHrSSy>r4)L#q$E@o$E!NLfiB01b{HL*el z22g_06!&n-K@cc}dZB(#z29yRw`4ep>&c#_yZa&V$l8mOHE8n~OS-NOK791Z1*jfS z2uO5-pm7@f&Y^cfND;BLu;2==8mJRgl9-qZaG5?f(EZq!`mwg(dOE~JDJ}*uH|b&z zS=y(N@JKqX-v{uiQ@cVSRHKDhzon|IbsBqfrIO!P~t1i{C z`d%G8%^v0N0A{J@24RwBoU6bh0y>R~iVCp+fCk71Z~R56^O8cbX7|n3>6)?LoS{`J z(Lkagxj0rTCZ--}v-)fY0>ck0Bqt{y9W>GK`vcyT{tLVTUH#Wc&3yuFDLiQliw8nn zu)!ro^!@7f3++9GpTRwQ)d{f(H)Ipv$L;tfybj||P`BzK!+3b`@E^d|{zv+hSpCS+ z&CSjV2kw-X+S~gy5U(^XLoR;ySt>lLPoJI#ON0@9{u{~^+%oIc($(1+*0v`RJFdDN zC`R3Hz@OM;Sh)v#26K&{msd?yl@$YzfWQsPeZ^-l0AJAa1k=k9-3gF88!90b<)abA zr<#=+bTnl$p2$Zt`;MS7#7s_HR8&uT1;N7jY!kfiFcDML)kPXQ8Dtg}MV^D9u>l!R$f95CbL=D~C6&l!i*d6FGA2>FA=Yo+>Qbxx^ycr+i}(XV#N-c6a9oFrt_j7(1MRk)3h9}To4GV=H8 z>KLq{#Az@%tdU&IUm)KCvB}k)&A;kyfOO%16w3Tzv67AnxQC}d^3 zK<5CX$W|pGApyEbPD;w}G%s}^H|?_>QA#eMaUPD#roXb+}c`b9|q`%{>~7U%)X-RXJdFntCuzKRRr$;q?-hz zy&%s(hYI8lx9!-Mrw`zKf_`TnZi71Zbqh#jtzlCjNFX4kpk4}fDtkR%o7$^`&Ys-+ z%wLjDeU{AEt$?_T*2 zZQZ{cE_p2VO}OP5>nCUw7i0Wcb5K0bPMlZwh&cuzRJF~%1CZ_O+bkc}@9OQfbJIc# zFapw)uhR#ZvBRVUw5z)P$~j}5612C$n%cUR?CtI0+>`j-1S(~lVtXH?*Nfnrm}vXE z`=&uAdUamEAO9RJU81cOD#)&aQuRFmeRP;FX`?lq6zmDKFB;Ad5}~5Sx9qPBKqv9H zp)ImW`y_@*huNS$3>%z=85Pc@+IJ9kLQ_*>UxwF4U%)RM7xrW2NaRr=ZbB!FU_7$? zNi#qN1veQSBG`Xgg^w0yA%9ql=)_b|jSJ#=AjRYcZG~b7XfaXa+VKibg@Nx;F4UbJ z9aPlNq=jM#RuNnoNL(0k`S9V68~XrO%Jb*X0bK&CZ(rDvNE0s{Zu_4#HvMlH`qbv< z+v@96a4y@8m6*zct|%>ae)jBl&1Suho}O*}S5JA{eQ>})a-4P-<)Ibjxoft&pJ zoK_!{v*l!D*cIa8+`yL>7u5@ofb5%h$Mu820_g;x7P^eJ+pVOFp^5e{GAg`)X#Byr zy3D{ANZC!KO80vzQUBD8>A95|L!_ydi;YU!o|iAQXilqha_BiYc46@4y%Y@`ff^u^1rf=z))z1!}QP`=&C6I)zl^smul4x${L1$BHnRD} zixUd6djN0<0;O7+#P>n{oH%8#SDr)hyX9%;l^g|RA9zsu99eCx|} z2Ms{RSfzd6+~nj4L~l=uurLQll%Vgy)|Lf*MD6DF@yd|)sj2#zW@Oew6_63^ zEiN%HUtXmqVKWi~8-8(q3XCENHUWS^g3vX576yiU)RK~I88PFkjvU%whkAZ-5B%4s zFDxw8Jo1&Q9@;Zhx_i48@Lc#?2(OSgZ&v%$+F*GiQQy9Ga=3Zf#nNCiSv|yK1t=5*GmNmoex{GMf;aO_ZJiGJ55TUK?Tzk(x0C~ zx5x|H=g6$eDX*^4QBPou08^r*Xh0$xRN5tNrWlZz|D_Q7!d92Zj|d4VE-%-EY6}hx zh9O*BT;$~BWZ%G*0vG#hQ-t*P?H_MW5L;i2g0tk8!w6futF(1>{)|`#UN%%9!Jojg zmXCHQBnhc(p5A`)j?jJ)MC+kU1^Vh@W>Xq^2mk9*oIDtL3k?lGEYrc2Z)s+bWS%^EVuA80==6{# z*>jQjU_E#eMlHc77zrmMu#Y3K1b4eRJXo$G1WFtf|KnKbKbQM|e&zK)&XWH1E&n*z zb#va}6oDX^{`3C@_)o*A|DzeNKNGC~L5Td*s{ZfS`QHzv{o6bKe|DGuKR)~K-txb{ z%L}Snv&=XFPxb^YZR2 Rs}zNh>yFtgaTNTXyYHZ?Q{QY*z$>zhkzA?UUc#iS22j-(o*Yvq~!`RG&pC|_WFROh#vm7yM^ju zfx7)2okc(}@eB|Ou9B?NOZni?SY~Lh3ifBO5d5eo$0=u~8+je~2xofW1jTmb=sr$- zAL{GtXZHZ}!VGNIkX1^u{Kk0YCV)0=T-?^zaeU|DbYGwRD$tS_yRtrB&P>yr()W?a zP&r*YTya3yyq%$NW3&#P>dMxwKnwl7vX$s)HN`D1EK(sS$ysh>i`W_w#?4oa&)vI( zGw=6=k`5!V*LN*hiM(8~eW)*e@3y&LJKIf|ME_@D-j+$YF%2d>xnhI1PTwCg)E|>Q z`q0_Q3F|(2GMQj`j?Cmzyh59bzyJ4ymy=lutE+nacugWGW#A(4#b+C2Jl4xm)t2o} z=+rbcHm57izpu@$Vp9GMQ@uVZkHFrQnH}jURn(6_t5L#!R)cX<#D1VDF!H(o*4IdFAU>Q^kXKoP=+R7S9Ug?N6sc+7cjG%s`YGX9?I3QpZNM&m436- z228Y};Y*}l{MUZg?2Q-@1uvLIgsIq6V!`ox-IXUJSK#m+z`$A%&E z>+1q&bY0kF5pCAHbkL7uj@5bk^pkq-=I#M`N*`fp%jE>-HwjYn$I}~34^Mw3vcZPM zl$_|JrSq#fw^CP611(qw7{t|dPGWNNG_!@&^4w}toZf*!j#u|n)9I$%hPf% zuDB43oH<%%vwUYV{DiP3k;_iAZ*IF@cq=U_Ns!;-0SAy|qAw>S+@6%5}HG4rS>73g0tw?PNX6lP%v2`|$o<&ik7$Yb+F!C_~o_lgN*b@rRd zqA30rt0G2+?lm{D(J;3TH;5;WU)|VLQ_lgY@@~FKNPi=g(6?PdMO}T2-u@$Gf)<6}?w^R!=S{uHh_wNmBp4aXE_&kFO(!^MWAXxD{g1%+@@*UKP5_NTIb zotqQI#qUNtkzu0E1hc%(QyA!^Uk0o$JD*&8_rmYUuc5HA?R2xw_y=kGW2(OWI8u&K za{L|Lh?Zi?*6!rdY~z}{;-{7>bkm)E_Fs9pHQW{ZxK>syk%c$wW{$T1Kr{dW_O01_ zGSP3yFUAYAnVAcOrs^|fc|CpyRoLpTo$qZ7MJ1)HtJF|$3|yRxocWGlzj=g3%15(5 zFYPv1Dp=FXq{GfFB zuRLt-Y}LiT{QUa$tGcGfeSK_be7pvpR)4NB7nK!^lEIIa6-yi`Pc+jc)Y%t=BfsZ@omjI&;(BhyMBp zBOLs#2Wq(QtXi~f8`mt=Zs+GeICwm!&5)&u-{|l#_#{u+hv4*1iqk`?yEIg7 zw7VBX7&tg-__BueDd$^SZszXhel`3J$K>(i) zpWfm0l6!nqwleMKoHgI-dTXY-oDJZDqz`qHLd1BPsqU7CEJJST((AdGbOLKX#%o>2 z15z0nG<;pEj(#3}`liJ(CFp!Y_(pl<9-3|1R9wwN8X9EdtJ8Os-GpeR?_R#XT~B_e zme!0sNuf-D3-S0?X>XEVz0G!ae5tW9cjeo9*W?$X9VkQ?5_bAZt=V!)w(EPAD$sJ| zr=T-v@HeP?`3t`MB9^Kx+|YkuNv*-^(*P%yz`#gV*pjJPn#SRM<_0?_o4veM9 z_qRWONT1dVJhR^{;xZdz@VHxCgE}zOZ}A9tD|`)tMtW>~Jc>qI>4Pq_#)CV3-Y=tN zOr@nmHVB-d$Bd31B6gKZHCq*Ct)R>FuEXT|6BvP-YVc1{$jlvDJvs4Wj$NCbO?~>{ zgG@S5eoaQRVohz8H0Y7T{Pe`cL`R1-b1ZzLqgOqZ>d6IhyeEsF5^>l%zgry#uS*l1 z_2I^N;r^Y+x9;Ogi<~hFU-oa;=zLUSE-2WP#};-ziABW|71FP)U!HAor7VocXQSh1 z;P5-tCnY5{Nh%u6fP^sBIOAz{JF(|&oFKQ+!Njbrcy^GAI-`z}scC$a!O{1zREfF{ zWQKH9CRbPDAyT@%S4i^q>(bJ$U>qRWnC{Nb!$Z8Dp1h%ik}$>QeI7WH*_$_)3RB&i z89h3vFQ2@hf16Y@jdq(%@MI1-x|_Vulb+$Hq3>7e?|(WmK?Z$<#f>Lv{WiAiPR_k3 zH*RPEfL2#OLOau8xqjt!WRID(wd-8#{`~xxtu4#lMJBNbJ6G5JoFTTcF5+U%(6oE@ zQ;g(X-K3A7nVHoaOJ04|h(H93l?SQRCvp4FbgEe+5U@39aHX}!l(yJciBSsJ{IUOx z*Gs?{B}w>A@p~{QgA&0K)#2iIHKh;wDek_$=fE}Kt2*HlYvYxLAZcJ&Ra8_|PcKQ6 zQG(?aj9OScg7l>P;0|txShi|HTpVx(Vj`kXkDg|$0xwp$eLbbm{|4r^%&7d`IUDH6 zUE$U^6z`WU=`~FjNG@0tuJT*gb}nV4p^=kUvLJqBQB+lR9B?Lpc)`|XGQPxb9~*6Z zG(Y!HWr|qX&uER;HK%`DB&~nNg)jW(vWrN{PL`7Y)aixtsQiF=H{;i*K8)-0!vCj( zbB|~0|Ks>^`DsyZQJTIXLULKI`6`zQO<{5g8ABm=l4}VKjgb39uAwC)_uJ+!a!KyA zSZJ-e%cRTvK7IGtU)%X?XM3FUdcV)>{d}G45jQ7Vc2gvEa4}pmQY$#KTaFuVA*CgTF`YAtwK~>d24*?&+)CQvO#+fe0-XLk-)_OMK1pctjRUF> z+xKlT4EhNln>@+>5`VX>*t}a+bmqXz_!n>Y@*FsvHGK4Lh+H)^{+29ILz~1K?303a zY-UFvKRyCMG1sEH>7TzDBkaBfEgM(cRYYjH=Q42;g-)|JS$RB3C@hQXJv}h|zekh3 z18`1&$qraR|F|nvoY5g~qvtwgpFPy9jUjH;JZ;Jw}#LR{Ox|fmCgO4vfOOfn5WCfGBve{ z1$+xizLEoa; zM6PEYA3hl0ctawQOuWNw?MUg#ye&Bh%qjUo@;N6rx3W=pP`LneBPEDGf5P-YO&k&? z%pJ2vg`oJNuYqA!RvAEYii?Yh#50M8<>lp|wjp2s4+4B0tf&W7G3pMc%qb}Xvj(Zp z%gY%c#b9n_)kE7>gAa+K1^dhnYGq!#!Yhb-ad57Fm7uJ-l$Pu=U0V9~zLUoQ8*~re zGCFogSJX}5U%9;n*49=_Wu=dQzV`<68zE%i{YfZ%>r%5}akhwHy^8ke z%#6W)cszV3OgZ%Xd85!hioYHkW2$dB-u;4EOl(VI25V7us^|C z0*Qz~!?{uGDwW-X#`TaG62*iW8@16S0a=|z_?-3H6@MlLSdUDV|8VPxHF7<7B|5_1rVeq*NAvL zKCiDRN++QQnQ{oUtVa_72I`V>8HWVH(C3G`ovSD7-GTA=^5rQQySI~aHV6cDQN>O4 z`7f2#AG=#@f#ld}kP^r7CjCBr<+_m25=Qo|{@ejx_)RQ!V}9;INJvOg&+M#gAG3nY z3OkJa@oT+7Ft~3dS1o*<^hXe7vEs(4D?9b|I}KwgopE%DrV#J&G{;e-!9O7_ZTre$ z)4IekmCQ@zi26x&b>N4-AQC4jiWl+^Lw2&hC$;Vt3lk zHm&AK!x5y-J>pkt7d)z+OkpH#Z33J<2~AjCR*RHsG862w(T@uQ&5?G9pQHGOXO2Ic z1kUf>m&WMdtDe>>CMu5V~A#m8ur z02owQ$o3DC9|th9%Th(7?w7?L^*Dp84=1N6`QY!-0-PO-n;sq>Ky<)I%E*~R5|KLj z=gX3J9U2Bd?(u+Y-ewj<&BB70^ND^ckIti?TL~A_pTaLn>&j)Ey!`X!%Qwz{-29C* z0)~vvf(|dU!lvK3I&=TdM5c*9n{VRblR9irJ2e2DfrQ-LrL!AjaBhVzKMUrJyoB;= z2Z_e_A6{q(HmGS$8xvcHh75U$u5wXz!3e%J{By zJS8O|kO#)e0<0q~VCW;|Vt%|GM`T17A$4o$dB26NytSU5XUKf`v>zXzSu|E^b-oXa zpa;&*sFYb)kU5~YH?Lvhgylx4Pt3&>SabY@W{Sdhw#K;V+O-XGH|Im8T~I=sF;(fc^s#Q#;=gDF68QO;^`m;KV+D z49)_3MK^@Vg_9%0!yta_=~iygmWsz8%o(bD^=fH*Pd0TlbglGu+Qa1M`JsB%Wo2ds zqwYBkJ~{uMu{=u!H5Q<0WV4I8dCVF-zQ3SDznNaNy)8{AGP+>y>1*P-FSIv>Gvy+jSOEgk8y@^)I`}5sD8EI0r|Z~ z2ETTYZo#Ryv1cl(-rc63vooCXPF%xWoY^m5FKPMO@wW z%BBVU+-;Z_&TkAh>x|wLC@nn^%$M9nG&-tCxU=(ZxxKw%Zu|(u|4&=GRDXK9Z^Z9& z6BNpw?abA8Sq2QvnOK&uY@XbSu`{rk&d5k;+>;!H9=F@on{rn*E)gtA_ap*2%({IooFc=$p(p|z~FvzYKlRW8TYvB ze`6P|Z?d0DGo~>~Vdt6avbo`YR0y8`{Ud{Uzcrksq zYE!;dDv80UyE@NSbkV0v$or=QSZM`@cb^eEeOWZJl4M(+s*WaTMVj;TLT1inG<&(2 z^NE~u2^ip`Q&vdDpzS4eGd|l}D}F`gBRj~Az1ZgF)0>pt+con!>=G-c56Hi?4?kFh zgXcp!tPp+Pb&z1nZ69m`{vC7k6jl@?xNPVvGs@E`h?#%T-Yjn`$?{Z>uLVsaO9P1E z=XN!&vE|owtG~ul#Ms$6TDEm1wB{!3l}J+$f?ub$2VRqL&q+&D0rRE2u_ch#0TtuE zajF!3?Mr;~=voDgLa|4s-mB|sYrXJcEP}Z$nmAuNEUBY2_3j& z7a@R~_Xv73LBTx4bwf}=)hgZD|L?YjS(!?myL@)>x1zv$5zN{xX=jPQZ~XlV1R;yI YZ{Fw3Erqdb2A)2IGBCYViNr?#AEvm3S^xk5 literal 0 HcmV?d00001 diff --git a/doc/images/pairingwithpin.png b/doc/images/pairingwithpin.png new file mode 100644 index 0000000000000000000000000000000000000000..73616f86aec45bc18e1dfb10e926fa5844f74ccd GIT binary patch literal 37261 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 literal 0 HcmV?d00001 diff --git a/doc/images/trayicon.png b/doc/images/trayicon.png new file mode 100644 index 0000000000000000000000000000000000000000..4c9ba3e10b817ab0f4f4052d4bff624e0aadf449 GIT binary patch literal 25880 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 Oe?I6Sx{c-6*xvxj1e9R_ literal 0 HcmV?d00001 diff --git a/src/AUTHORS b/src/AUTHORS new file mode 100644 index 0000000..91f72db --- /dev/null +++ b/src/AUTHORS @@ -0,0 +1 @@ +Emanoil Kotsev diff --git a/src/interfaces/org.bluez.MediaPlayer1.xml b/src/interfaces/org.bluez.MediaPlayer1.xml new file mode 100644 index 0000000..1ce4d59 --- /dev/null +++ b/src/interfaces/org.bluez.MediaPlayer1.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/interfaces/org.bluez.adapter.xml b/src/interfaces/org.bluez.adapter.xml new file mode 100644 index 0000000..4cdd6a5 --- /dev/null +++ b/src/interfaces/org.bluez.adapter.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/interfaces/org.bluez.device.xml b/src/interfaces/org.bluez.device.xml new file mode 100644 index 0000000..7715379 --- /dev/null +++ b/src/interfaces/org.bluez.device.xml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/interfaces/org.bluez.manager.xml b/src/interfaces/org.bluez.manager.xml new file mode 100644 index 0000000..5c16ffa --- /dev/null +++ b/src/interfaces/org.bluez.manager.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/interfaces/org.bluez.obex.Agent1.xml b/src/interfaces/org.bluez.obex.Agent1.xml new file mode 100644 index 0000000..e7a0289 --- /dev/null +++ b/src/interfaces/org.bluez.obex.Agent1.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/interfaces/org.bluez.obex.client.xml b/src/interfaces/org.bluez.obex.client.xml new file mode 100644 index 0000000..1eadf29 --- /dev/null +++ b/src/interfaces/org.bluez.obex.client.xml @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/interfaces/org.freedesktop.DBus.ObjectManager.xml b/src/interfaces/org.freedesktop.DBus.ObjectManager.xml new file mode 100644 index 0000000..cf73bea --- /dev/null +++ b/src/interfaces/org.freedesktop.DBus.ObjectManager.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/interfaces/org.mpris.MediaPlayer2.xml b/src/interfaces/org.mpris.MediaPlayer2.xml new file mode 100644 index 0000000..12d1099 --- /dev/null +++ b/src/interfaces/org.mpris.MediaPlayer2.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/interfaces/org.tdebluez.agent.xml b/src/interfaces/org.tdebluez.agent.xml new file mode 100644 index 0000000..36bac01 --- /dev/null +++ b/src/interfaces/org.tdebluez.agent.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libtdebluez/CMakeLists.txt b/src/libtdebluez/CMakeLists.txt new file mode 100644 index 0000000..bb80327 --- /dev/null +++ b/src/libtdebluez/CMakeLists.txt @@ -0,0 +1,96 @@ +################################################# +# +# (C) 2018 Emanoil Kotsev +# deloptes (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +project( libtdebluez ) +set(LIBRARY_VERSION 0.0.1) + +# include( ConfigureChecks.cmake ) +foreach( f ${TQT_LIBRARIES} ) + if( ${f} STREQUAL "tqt-mt" ) + set(TQUI_LIBRARIES "tqui" CACHE TYPE STRING FORCE) + endif() + if( ${f} STREQUAL "qt-mt" ) + set(TQUI_LIBRARIES "qui" CACHE TYPE STRING FORCE) + endif() +endforeach() + +# import required +#tde_import( lib... ) + +add_subdirectory( interfaces ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR} + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} + ${DBUS_INCLUDE_DIRS} + ${DBUS_TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + +##### headers ################################### +# implementations +install( + FILES adapterImpl.h + btuuids.h + deviceImpl.h + devicemimeconverter.h + objectmanagerImpl.h + DESTINATION ${INCLUDE_INSTALL_DIR}/tdebluez ) + +##### other data ################################ +# install( FILES xxxxxxxx.kcfg DESTINATION ${KCFG_INSTALL_DIR} ) + +##### tdebluetoothwidgets (module) #################### +# +# add_custom_command( OUTPUT tdebluetoothwidgets.cpp +# COMMAND +# ${KDE3_MAKETDEWIDGETS_EXECUTABLE} +# -o tdebluetoothwidgets.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/tdebluez.widgets +# DEPENDS +# ${CMAKE_CURRENT_SOURCE_DIR}/tdebluez.widgets ) +# +# set_source_files_properties( tdebluetoothwidgets.cpp PROPERTIES COMPILE_FLAGS "-DQT_PLUGIN" ) +# +# tde_add_kpart( tdebluetoothwidgets +# SOURCES tdebluetoothwidgets.cpp +# LINK tdebluez-shared +# DESTINATION ${PLUGIN_INSTALL_DIR}/plugins/designer +# ) + +set( target tdebluez ) + +set( ${target}_SRCS + objectmanagerImpl.cpp + adapterImpl.cpp + deviceImpl.cpp + devicemimeconverter.cpp +# agent/introspectableinterface.cpp agent/org_trinitydesktop_tdeblueznode.cpp + ) + +##### tdebluez (shared) ########################### +# set( KDE3_DCOPIDL_EXECUTABLE ${KDE3_DCOPIDLNG_EXECUTABLE} ) +tde_add_library( ${target} SHARED AUTOMOC + SOURCES ${${target}_SRCS} + VERSION ${LIBRARY_VERSION} + DEPENDS bluezinterfaces-static + LINK ${DBUS_TQT_LIBRARIES} tdeparts-shared bluezinterfaces-static ${TQUI_LIBRARIES} + DESTINATION ${LIB_INSTALL_DIR} + ) + +##### install import cmake modules ############### +tde_install_export( ) diff --git a/src/libtdebluez/README b/src/libtdebluez/README new file mode 100644 index 0000000..fa8b639 --- /dev/null +++ b/src/libtdebluez/README @@ -0,0 +1,29 @@ +interfaces + source interfaces for autogenerating with dbusxml2qt3 +libtdebluez/ + Implementations and other files part of the library + +libtdebluez/ + +libtdebluez/interfaces + interfaces autogenerated with dbusxml2qt3 + - autogenerated proxy to objectmanager + - adapter + adapter1 proxy + gattmanager1 proxy + media1 proxy + neworkserver1 proxy + - obexagent + autogenerated obex agent - interface and proxy + - properties + autogenerated dbus properties proxy + - device + autogenerated device1 proxy + autogenerated mediacontrol1 proxy + - agent + autogenerated agent1 interface and proxy + autogenerated introspectable interface + autogenerated trinity desktop tdebluez node interface + - manager + autogenerated proxies to managers + \ No newline at end of file diff --git a/src/libtdebluez/adapterImpl.cpp b/src/libtdebluez/adapterImpl.cpp new file mode 100644 index 0000000..703f06f --- /dev/null +++ b/src/libtdebluez/adapterImpl.cpp @@ -0,0 +1,144 @@ +/* + * + * Adapter Implementation of bluez5 for libtdebluez + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of libtdebluez. + * + * libtdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * libtdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include + +#include +#include +#include + +#include +#include + +#include "adapterImpl.h" + +namespace TDEBluetooth +{ + +AdapterImpl::AdapterImpl(const TQString& service, const TQString& path, TQObject* parent, const char* name) : + Adapter1Proxy(service, path, parent, name) /*,properties(service,path,parent,name)*/ +{ +} + +AdapterImpl::~AdapterImpl() +{ +} + +void AdapterImpl::powerOn(bool state) +{ + // https://www.kernel.org/doc/Documentation/rfkill.txt + // http://jwhsmith.net/2015/02/manipulating-rfkill-using-devices-programmatically/ + // https://cpp.hotexamples.com/examples/-/-/rfkill_alloc/cpp-rfkill_alloc-function-examples.html + // https://github.com/systemd/systemd/blob/main/src/rfkill/rfkill.c + + TQString device = getPath(); + device = device.replace(TQRegExp("^/.*/"), ""); + int hcidx = -1; + TQDir d("/sys/class/rfkill"); + d.setFilter(TQDir::Dirs); + for (int i = 0; i < d.count(); i++) + { + // expected is rfkill + TQFile f("/sys/class/rfkill/" + d[i] + "/name"); + TQString content; + if (f.exists() && f.open(IO_ReadOnly)) + { + TQTextStream stream(&f); + content = stream.readLine(); + f.close(); + } + else + { + continue; + } + if (content.startsWith(device)) + { + TQFile f("/sys/class/rfkill/" + d[i] + "/index"); + if (f.exists() && f.open(IO_ReadOnly)) + { + TQTextStream stream(&f); + hcidx = stream.readLine().toUInt(); + f.close(); + } + break; + } + } + + if (hcidx < 0) + { + // error handling + tqDebug("Index for the device %s not found", device.local8Bit().data()); + return; + } + + struct rfkill_event event = { 0 }; + + TQFile file("/dev/rfkill"); + if (!file.open(IO_ReadWrite)) + { + tqDebug("Failed to open %s", file.name().utf8().data()); + return; + } + + event.idx = hcidx; + event.op = RFKILL_OP_CHANGE; + if (state) + event.soft = 0; + else + event.soft = 1; + + tqDebug("Bluetooth device %s switches: idx(%i), soft(%d).", device.local8Bit().data(), event.idx, event.soft); + + if (write(file.handle(), &event, sizeof(event)) < 0) + tqDebug("Failed to write to %s", file.name().utf8().data()); + file.close(); +} + +TQString AdapterImpl::getPath() +{ + return TQString(m_baseProxy->path()); +} + +void AdapterImpl::slotSetAlias(const TQString& alias) +{ + TQT_DBusError error; + setAlias(alias, error); + if (error.isValid()) + tqDebug("AdapterImpl::slotSetAlias(%s) failed: %s", alias.utf8().data(), error.message().utf8().data()); +} + +void AdapterImpl::slotSetTimeout(int timeout) +{ + TQT_DBusError error; + setDiscoverableTimeout(timeout, error); + if (error.isValid()) + tqDebug("AdapterImpl::slotSetTimeout(%i) failed: %s", timeout, error.message().utf8().data()); +} + +} // namespace TDEBluetooth + +#include "adapterImpl.moc" +// End of File diff --git a/src/libtdebluez/adapterImpl.h b/src/libtdebluez/adapterImpl.h new file mode 100644 index 0000000..e192a17 --- /dev/null +++ b/src/libtdebluez/adapterImpl.h @@ -0,0 +1,59 @@ +/* + * + * Adapter Implementation of bluez5 for libtdebluez + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of libtdebluez. + * + * libtdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * libtdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#if !defined(ADAPTERIMPL_H_INCLUDED) +#define ADAPTERIMPL_H_INCLUDED + +#include "interfaces/adapter1Proxy.h" + +using namespace org::bluez; + +namespace TDEBluetooth +{ + +class AdapterImpl: public Adapter1Proxy +{ + Q_OBJECT + +public: + AdapterImpl(const TQString& service, const TQString& path, TQObject* parent = 0, const char* name = 0); + + virtual ~AdapterImpl(); + void powerOn(bool state); + TQString getPath(); + +public slots: + void slotSetAlias(const TQString& alias); + void slotSetTimeout(int timeout); + +}; +// class AdapterImpl + +}; +// namespace TDEBluetooth + +#endif //ADAPTERIMPL_H_INCLUDED + +// End of File diff --git a/src/libtdebluez/btuuids.h b/src/libtdebluez/btuuids.h new file mode 100644 index 0000000..597f037 --- /dev/null +++ b/src/libtdebluez/btuuids.h @@ -0,0 +1,148 @@ +/* + * + * List of BT UUIDs and resolver for libtdebluez + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of libtdebluez. + * + * libtdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * libtdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +/* + * This was taken from bluez/lib/uuid.h 2018-07-22 + * + */ + + +#include +#include + +#ifndef BTUUIDS_H_ +#define BTUUIDS_H_ + +static const std::map my_map = +{ + { "00001203-0000-1000-8000-00805f9b34fb", "Generic Audio" }, + { "00001108-0000-1000-8000-00805f9b34fb", "Headset" }, + { "00001112-0000-1000-8000-00805f9b34fb", "Headset AG" }, + + { "0000111e-0000-1000-8000-00805f9b34fb", "Handsfree" }, + { "0000111f-0000-1000-8000-00805f9b34fb", "Handsfree AG" }, + + { "0000110d-0000-1000-8000-00805f9b34fb", "Advanced Audio" }, + + { "0000110a-0000-1000-8000-00805f9b34fb", "A2DP Source" }, //Advanced Audio Distribution Profile + { "0000110b-0000-1000-8000-00805f9b34fb", "A2DP Sink" }, + + { "0000110e-0000-1000-8000-00805f9b34fb", "A/V Remote Ctrl" }, //Audio/Video Remote Control Profile + { "0000110c-0000-1000-8000-00805f9b34fb", "A/V Remote Ctrl Tgt" }, + + { "00001115-0000-1000-8000-00805f9b34fb", "PANU" }, + { "00001116-0000-1000-8000-00805f9b34fb", "NAP" }, + { "00001117-0000-1000-8000-00805f9b34fb", "GN" }, + { "0000000f-0000-1000-8000-00805f9b34fb", "BNEP Service" }, //Bluetooth Network Encapsulation Protocol + + { "00002a50-0000-1000-8000-00805f9b34fb", "PNPID" }, + { "0000180a-0000-1000-8000-00805f9b34fb", "Device Information" }, + + { "00001801-0000-1000-8000-00805f9b34fb", "GATT" }, //Generic Access Profile (Generic Attributes) + { "00001802-0000-1000-8000-00805f9b34fb", "Immediate Alert" }, + { "00001803-0000-1000-8000-00805f9b34fb", "Link Loss" }, + { "00001804-0000-1000-8000-00805f9b34fb", "TX Power" }, + + { "0000112d-0000-1000-8000-00805f9b34fb", "SAP" }, + + { "0000180d-0000-1000-8000-00805f9b34fb", "Heart Rate" }, + { "00002a37-0000-1000-8000-00805f9b34fb", "Heart Rate Measurement" }, + { "00002a38-0000-1000-8000-00805f9b34fb", "Body Sensor Location" }, + { "00002a39-0000-1000-8000-00805f9b34fb", "Heart Rate Control Point" }, + + { "00001809-0000-1000-8000-00805f9b34fb", "Health Thermometer" }, + { "00002a1c-0000-1000-8000-00805f9b34fb", "Temp Measurement" }, + { "00002a1d-0000-1000-8000-00805f9b34fb", "Temp Type" }, + { "00002a1e-0000-1000-8000-00805f9b34fb", "Immediate Temp" }, + { "00002a21-0000-1000-8000-00805f9b34fb", "Measurement Interval" }, + + { "00001816-0000-1000-8000-00805f9b34fb", "Cycling SC" }, + { "00002a5b-0000-1000-8000-00805f9b34fb", "CSC Measurement" }, + { "00002a5c-0000-1000-8000-00805f9b34fb", "CSC Feature" }, + { "00002a5d-0000-1000-8000-00805f9b34fb", "Sensor Location" }, + { "00002a55-0000-1000-8000-00805f9b34fb", "SC Control Point" }, + + { "00000003-0000-1000-8000-00805f9b34fb", "RFCOMM" }, + + { "00001400-0000-1000-8000-00805f9b34fb", "HDP" }, + { "00001401-0000-1000-8000-00805f9b34fb", "HDP Source" }, + { "00001402-0000-1000-8000-00805f9b34fb", "HDP Sink" }, + + { "00001124-0000-1000-8000-00805f9b34fb", "HID" }, + + { "00001103-0000-1000-8000-00805f9b34fb", "DUN Gateway" }, //Dial-up Networking Profile + + { "00001800-0000-1000-8000-00805f9b34fb", "GAP" }, //Generic Access Profile + { "00001200-0000-1000-8000-00805f9b34fb", "PNP" }, + + { "00001101-0000-1000-8000-00805f9b34fb", "SPP" }, //Serial Port Profile + + { "00001104-0000-1000-8000-00805f9b34fb", "OBEX Sync" }, + { "00001105-0000-1000-8000-00805f9b34fb", "OBEX OPP" }, + { "00001106-0000-1000-8000-00805f9b34fb", "OBEX FTP" }, + { "f9ec7bc4-953c-11d2-984e-525400dc9e09", "OBEX DIR" }, + { "0000112e-0000-1000-8000-00805f9b34fb", "OBEX PCE" }, + { "0000112f-0000-1000-8000-00805f9b34fb", "OBEX PSE" }, + { "00001130-0000-1000-8000-00805f9b34fb", "OBEX PBAP" }, + { "00001132-0000-1000-8000-00805f9b34fb", "OBEX Msg Access Srv" }, + { "00001133-0000-1000-8000-00805f9b34fb", "OBEX Msg Notif. Srv" }, + { "00001134-0000-1000-8000-00805f9b34fb", "OBEX MAP" }, + // taken from + // https://www.vistax64.com/threads/bluetooth-peripheral-device-cannot-be-found.62944/ + // https://together.jolla.com/question/64565/accessing-bluetooth-profiles/ + // http://www.sensi.org/~ak/tmp/n95.txt + { "00005005-0000-1000-8000-0002ee000001", "Nokia PC Suite" }, + { "00005601-0000-1000-8000-0002ee000001", "Nokia SyncML Server" }, + { "00000001-0000-1000-8000-0002ee000001", "SyncML Server" }, + { "00000002-0000-1000-8000-0002ee000002", "OBEX Syncevolution" }, + { "00000004-0000-1000-8000-0002ee000002", "SyncML DM Client" } + +}; + +/*! + * This function resolves UUID to human readable service name. + * \return string service name + * \retval the service name + */ + +static const TQString resolveUUID(const TQString &uuid) +{ + + TQString name; + + for (auto i = my_map.begin(); i != my_map.end(); ++i) + { + if (i->first == uuid.latin1()) + { + name = TQString(i->second.c_str()); + break; + } + } + // name = i18n("Unknown"); + return (!name.isEmpty()) ? name : uuid; +} + +#endif // BTUUIDS_H_ diff --git a/src/libtdebluez/deviceImpl.cpp b/src/libtdebluez/deviceImpl.cpp new file mode 100644 index 0000000..7b8a971 --- /dev/null +++ b/src/libtdebluez/deviceImpl.cpp @@ -0,0 +1,49 @@ +/* + * + * Device Implementation of bluez5 for libtdebluez + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of libtdebluez. + * + * libtdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * libtdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "deviceImpl.h" + +namespace TDEBluetooth +{ + +DeviceImpl::DeviceImpl(const TQString& service, const TQString& path, TQObject* parent, const char* name) : org::bluez::Device1Proxy(service, path, parent, name) +{ + m_path = path; +} + +DeviceImpl::~DeviceImpl() +{ +} + +const TQString DeviceImpl::getPath() +{ + return m_path; +} + +}; +// namespace TDEBluetooth + +#include "deviceImpl.moc" +// End of File diff --git a/src/libtdebluez/deviceImpl.h b/src/libtdebluez/deviceImpl.h new file mode 100644 index 0000000..45b355b --- /dev/null +++ b/src/libtdebluez/deviceImpl.h @@ -0,0 +1,62 @@ +/* + * + * Device Implementation of bluez5 for libtdebluez + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of libtdebluez. + * + * libtdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * libtdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#if !defined(DEVICEIMPL_H_INCLUDED) +#define DEVICEIMPL_H_INCLUDED + +// QT - Header +#include + +// debug +#include + +#include "interfaces/device1Proxy.h" +#include "interfaces/propertiesProxy.h" + +namespace TDEBluetooth +{ + +class DeviceImpl: public org::bluez::Device1Proxy +{ + Q_OBJECT + +public: + DeviceImpl(const TQString& service, const TQString& path, TQObject* parent = 0, const char* name = 0); + + virtual ~DeviceImpl(); + const TQString getPath(); + +private: + TQString m_path; + +}; +// class DeviceImpl + +}; +// namespace TDEBluetooth + +#endif //DEVICEIMPL_H_INCLUDED + +// End of File diff --git a/src/libtdebluez/devicemimeconverter.cpp b/src/libtdebluez/devicemimeconverter.cpp new file mode 100644 index 0000000..b14669e --- /dev/null +++ b/src/libtdebluez/devicemimeconverter.cpp @@ -0,0 +1,147 @@ +/* + * + * Device Mime Converter for libtdebluez + * + * Copyright (C) 2003 by Fred Schaettgen + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of libtdebluez. + * + * libtdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * libtdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +#include "devicemimeconverter.h" + +namespace TDEBluetooth +{ + +DeviceMimeConverter::DeviceMimeConverter() +{ + getIconName("bluetooth/unknown-device-class"); + getIconName("bluetooth/misc-device-class"); + getIconName("bluetooth/computer-device-class"); + getIconName("bluetooth/phone-device-class"); + getIconName("bluetooth/lan-device-class"); + getIconName("bluetooth/av-device-class"); + getIconName("bluetooth/peripheral-device-class"); + getIconName("bluetooth/mouse-device-class"); + getIconName("bluetooth/keyboard-device-class"); + getIconName("bluetooth/imaging-device-class"); +} + +void DeviceMimeConverter::getIconName(TQString mime) +{ + TQString iconName = KMimeType::mimeType(mime)->icon(TQString::null, false); + mimeTypeToIconMap[mime] = iconName; +} + +DeviceMimeConverter* DeviceMimeConverter::getInstance() +{ + static DeviceMimeConverter instance; + return &instance; +} + +TQString DeviceMimeConverter::classToIconName(int n) +{ + return DeviceMimeConverter::mimeTypeToIcon(DeviceMimeConverter::classToMimeType(n)); +} + +/* + * device classes + * + AUDIO_VIDEO (Value: 0x00000400) + COMPUTER (Value: 0x00000100) + HEALTH (Value: 0x00000900) + IMAGING (Value: 0x00000600) + MISC (Value: 0x00000000) + NETWORKING (Value: 0x00000300) + PERIPHERAL (Value: 0x00000500) + PHONE (Value: 0x00000200) + TOY (Value: 0x00000800) + UNCATEGORIZED (Value: 0x00001f00) + WEARABLE (Value: 0x00000700) +*/ + +TQString DeviceMimeConverter::classToMimeType(int n) +{ + TQString mimeType; + int major = ((n & 0x001F00) >> 8); + int minor = ((n >> 2) & 0x30); + switch (major) + { + case 0x00: + mimeType = "bluetooth/misc-device-class"; + break; + case 0x01: + mimeType = "bluetooth/computer-device-class"; + break; + case 0x02: + mimeType = "bluetooth/phone-device-class"; + break; + case 0x03: + mimeType = "bluetooth/lan-device-class"; + break; + case 0x04: + mimeType = "bluetooth/av-device-class"; + break; + case 0x05: + switch (minor) + { + case 0x10: + mimeType = "bluetooth/keyboard-device-class"; + break; + case 0x20: + mimeType = "bluetooth/mouse-device-class"; + break; + default: + mimeType = "bluetooth/peripheral-device-class"; + } + break; + case 0x06: + mimeType = "bluetooth/imaging-device-class"; + break; + case 0x07: + mimeType = "bluetooth/wearable-device-class"; + break; + case 0x08: + mimeType = "bluetooth/toy-device-class"; + break; + case 0x09: + mimeType = "bluetooth/health-device-class"; + break; + default: + mimeType = "bluetooth/unknown-device-class"; + } + return mimeType; +} + +TQString DeviceMimeConverter::mimeTypeToIcon(TQString mime) +{ + DeviceMimeConverter* c = DeviceMimeConverter::getInstance(); + if (c->mimeTypeToIconMap.find(mime) != c->mimeTypeToIconMap.end()) + { + return c->mimeTypeToIconMap[mime]; + } + else + { + return c->mimeTypeToIconMap["bluetooth/unknown-device-class"]; + } +} + +} // TDEBluetooth diff --git a/src/libtdebluez/devicemimeconverter.h b/src/libtdebluez/devicemimeconverter.h new file mode 100644 index 0000000..63a1d9f --- /dev/null +++ b/src/libtdebluez/devicemimeconverter.h @@ -0,0 +1,53 @@ +/* + * + * Device Mime Converter for libtdebluez + * + * Copyright (C) 2003 by Fred Schaettgen + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of libtdebluez. + * + * libtdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * libtdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef DEVICEMIMECONVERTER_H +#define DEVICEMIMECONVERTER_H + +#include + +namespace TDEBluetooth +{ +/** + @author Fred Schaettgen + */ +class DeviceMimeConverter +{ +public: + static TQString classToMimeType(int deviceClass); + static TQString mimeTypeToIcon(TQString mimeType); + static TQString classToIconName(int deviceClass); +protected: + DeviceMimeConverter(); + static DeviceMimeConverter *getInstance(); +private: + std::map mimeTypeToIconMap; + void getIconName(TQString mimetype); +}; + +} // TDEBluetooth + +#endif diff --git a/src/libtdebluez/interfaces/CMakeLists.txt b/src/libtdebluez/interfaces/CMakeLists.txt new file mode 100644 index 0000000..2f44a83 --- /dev/null +++ b/src/libtdebluez/interfaces/CMakeLists.txt @@ -0,0 +1,135 @@ +################################################# +# +# (C) 2020 Emanoil Kotsev +# deloptes (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${TQT_INCLUDE_DIRS} + ${DBUS_TQT_INCLUDE_DIRS} +) + +set( INTROSPECTIONPATH ${CMAKE_SOURCE_DIR}/src/interfaces ) +set( DBUSXML2QT3_EXECUTABLE dbusxml2qt3 ) + +set( ObjectManager_HDRS objectmanagerInterface.h objectmanagerProxy.h introspectableInterface.h ) +set( ObjectManager_SRCS objectmanagerInterface.cpp objectmanagerProxy.cpp introspectableInterface.cpp) + +set( AgentManager_HDRS agentmanager1Interface.h agentmanager1Proxy.h healthmanager1Interface.h healthmanager1Proxy.h profilemanager1Interface.h profilemanager1Proxy.h ) +set( AgentManager_SRCS agentmanager1Interface.cpp agentmanager1Proxy.cpp healthmanager1Interface.cpp healthmanager1Proxy.cpp profilemanager1Interface.cpp profilemanager1Proxy.cpp ) + +set( Agent_HDRS agent1Interface.h agent1Proxy.h dbusbaseNode.h tdebluezNode.h ) +set( Agent_SRCS agent1Interface.cpp agent1Proxy.cpp dbusbaseNode.cpp tdebluezNode.cpp ) + +set( Adapter_HDRS adapter1Interface.h adapter1Proxy.h gattmanager1Interface.h gattmanager1Proxy.h media1Interface.h media1Proxy.h networkserver1Interface.h networkserver1Proxy.h propertiesInterface.h propertiesProxy.h) +set( Adapter_SRCS adapter1Interface.cpp adapter1Proxy.cpp gattmanager1Interface.cpp gattmanager1Proxy.cpp media1Interface.cpp media1Proxy.cpp networkserver1Interface.cpp networkserver1Proxy.cpp propertiesInterface.cpp propertiesProxy.cpp ) + +set( Device_HDRS device1Interface.h device1Proxy.h mediacontrol1Interface.h mediacontrol1Proxy.h) +set( Device_SRCS device1Interface.cpp device1Proxy.cpp mediacontrol1Interface.cpp mediacontrol1Proxy.cpp ) + +function( make_moc fileinput ) + add_custom_command( OUTPUT ${fileinput}.moc + COMMAND ${TMOC_EXECUTABLE} ${fileinput}.h -o ${fileinput}.moc + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${fileinput}.h + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_property( SOURCE ${CMAKE_CURRENT_BINARY_DIR}/${fileinput}.cpp APPEND + PROPERTY OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${fileinput}.moc ) +endfunction( ) + +#function( install_header fileinput target ) +# install( +# FILES ${CMAKE_CURRENT_BUILD_DIR}/${fileinput} +# DESTINATION ${target} +# ) +#endfunction( ) + + +##### ObjectManager ######################### +add_custom_command( + OUTPUT ${ObjectManager_HDRS} ${ObjectManager_SRCS} + COMMAND ${DBUSXML2QT3_EXECUTABLE} ${INTROSPECTIONPATH}/org.freedesktop.DBus.ObjectManager.xml 2>/dev/null + DEPENDS ${INTROSPECTIONPATH}/org.freedesktop.DBus.ObjectManager.xml + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) +make_moc ( objectmanagerProxy ) + +##### AgentManager ######################### +add_custom_command( + OUTPUT ${AgentManager_HDRS} ${AgentManager_SRCS} + COMMAND ${DBUSXML2QT3_EXECUTABLE} ${INTROSPECTIONPATH}/org.bluez.manager.xml 2>/dev/null + DEPENDS ${INTROSPECTIONPATH}/org.bluez.manager.xml + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) +make_moc ( agentmanager1Proxy ) +make_moc ( healthmanager1Proxy ) +make_moc ( profilemanager1Proxy ) + +##### Agent ######################### +add_custom_command( + OUTPUT ${Agent_HDRS} ${Agent_SRCS} + COMMAND ${DBUSXML2QT3_EXECUTABLE} ${INTROSPECTIONPATH}/org.tdebluez.agent.xml 2>/dev/null + DEPENDS ${INTROSPECTIONPATH}/org.tdebluez.agent.xml + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) +make_moc ( agent1Proxy ) + +##### Adapter ######################### +add_custom_command( + OUTPUT ${Adapter_HDRS} ${Adapter_SRCS} + COMMAND ${DBUSXML2QT3_EXECUTABLE} ${INTROSPECTIONPATH}/org.bluez.adapter.xml 2>/dev/null + DEPENDS ${INTROSPECTIONPATH}/org.bluez.adapter.xml + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) +make_moc ( adapter1Proxy ) +make_moc ( gattmanager1Proxy ) +make_moc ( networkserver1Proxy ) +make_moc ( propertiesProxy ) + +##### Device ######################### +add_custom_command( + OUTPUT ${Device_HDRS} ${Device_SRCS} + COMMAND ${DBUSXML2QT3_EXECUTABLE} ${INTROSPECTIONPATH}/org.bluez.device.xml 2>/dev/null + DEPENDS ${INTROSPECTIONPATH}/org.bluez.device.xml + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) +make_moc ( device1Proxy ) +make_moc ( propertiesProxy ) +make_moc ( mediacontrol1Proxy ) +make_moc ( media1Proxy ) + +tde_add_library( bluezinterfaces STATIC_PIC AUTOMOC + SOURCES ${ObjectManager_SRCS} ${AgentManager_SRCS} ${Agent_SRCS} ${Adapter_SRCS} ${Device_SRCS} + LINK ${DBUS_TQT_LIBRARIES} +) + +##### install headers ################################### + + +#file( GLOB _bin_dirs RELATIVE ${CMAKE_CURRENT_BINARY_DIR} +#      ${CMAKE_CURRENT_BINARY_DIR}/* ) +#unset( _exclude_dirs ) +#foreach( _dir IN LISTS _bin_dirs ) +#  if(IS_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${_dir} ) +#    list( APPEND _exclude_dirs PATTERN ${_dir} EXCLUDE ) +#  endif() +#endforeach() +# +#install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +# DESTINATION ${INCLUDE_INSTALL_DIR}/tdeobex +# USE_SOURCE_PERMISSIONS +# FILES_MATCHING PATTERN PATTERN "*.h" +# ${_exclude_dirs} +#) + +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DESTINATION ${INCLUDE_INSTALL_DIR}/tdebluez + USE_SOURCE_PERMISSIONS + FILES_MATCHING PATTERN PATTERN "*.h" + PATTERN "CMakeFiles" EXCLUDE) diff --git a/src/libtdebluez/objectmanagerImpl.cpp b/src/libtdebluez/objectmanagerImpl.cpp new file mode 100644 index 0000000..789e221 --- /dev/null +++ b/src/libtdebluez/objectmanagerImpl.cpp @@ -0,0 +1,607 @@ +/* + * + * Object Manager implementation of bluez5 + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of libtdebluez. + * + * libtdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * libtdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "objectmanagerImpl.h" +#include "btuuids.h" + +namespace TDEBluetooth +{ + +ObjectManagerImpl::ObjectManagerImpl(const TQString& service, const TQString& path, TQObject* parent, const char* name) : + ObjectManagerProxy(service, path, parent, name) +{ + agentManager = 0; + profileManager = 0; + healthManager = 0; + agentRegisteredStatus = false; + agentIsDefaultAgent = false; + // init connection to dbus + initDBUS(); +} + +ObjectManagerImpl::~ObjectManagerImpl() +{ + // close D-Bus connection + close(); + + if(agentManager) + delete agentManager; + if(profileManager) + delete profileManager; + if(healthManager) + delete healthManager; +} + +/*! + * This function try a reconnect to D-Bus. + * \return boolean with the result of the operation + * \retval true if successful reconnected to D-Bus + * \retval false if unsuccessful + */ +bool ObjectManagerImpl::reconnect() +{ + // close D-Bus connection + close(); + // init D-Bus conntection + return (initDBUS()); +} + +/*! + * This function return information about connection status to the DBUS daemon. + * \return boolean with the state of the connection to D-Bus + * \retval true if connected + * \retval false if disconnected + */ +bool ObjectManagerImpl::isConnectedToDBUS() +{ + return dBusConn.isConnected(); +} + +/*! + * This function returns pointer to connection of the DBUS. + * \return TQT_DBusConnection* of the connection to D-Bus + * \retval TQT_DBusConnection* + */ +TQT_DBusConnection* ObjectManagerImpl::getConnection() +{ + return &dBusConn; +} + +/*! + * This function close the connection to manager over the D-Bus daemon. + * \return boolean with the result of the operation + * \retval true if successful closed the connection + * \retval false if any problems + */ +bool ObjectManagerImpl::close() +{ + disconnect(this, SIGNAL(InterfacesAdded(const TQT_DBusObjectPath&, const TQT_DBusDataMap< TQString >&)), + this, SLOT(slotInterfacesAdded(const TQT_DBusObjectPath&, const TQT_DBusDataMap< TQString >& ))); + disconnect(this, SIGNAL(InterfacesRemoved(const TQT_DBusObjectPath& , const TQStringList& )), + this, SLOT(slotInterfacesRemoved(const TQT_DBusObjectPath& , const TQStringList& ))); + + for (PropertiesMap::iterator it = adapters.begin(); it != adapters.end(); + ++it) + { + org::freedesktop::DBus::PropertiesProxy *p; + p = it.data(); + if (p != NULL) + delete p; + } + for (PropertiesMap::iterator it = devices.begin(); it != devices.end(); + ++it) + { + org::freedesktop::DBus::PropertiesProxy *p; + p = it.data(); + if (p != NULL) + delete p; + } + adapters.clear(); + devices.clear(); + + dBusConn.closeConnection(DBUS_CONN_NAME); + return true; +} + +/*! + * This function initializes the connection to the D-Bus daemon. + * \return pointer to AgentManager1Proxy + */ +AgentManager1Proxy * ObjectManagerImpl::getAgentManager() +{ + return agentManager; +} + +/*! + * This function initializes the connection to the D-Bus daemon. + * \return pointer to ProfileManager1Proxy + */ +ProfileManager1Proxy * ObjectManagerImpl::getProfileManager() +{ + return profileManager; +} + +/*! + * This function initializes the connection to the D-Bus daemon. + * \return pointer to HealthManager1Proxy + */ +HealthManager1Proxy * ObjectManagerImpl::getHealthManager() +{ + return healthManager; +} + +/*! + * This function returns a list of objectpaths + * \return TQValueList + * \retval TQValueList + */ +ObjectManagerImpl::AdapterList ObjectManagerImpl::getAdapters() +{ + return adapters.keys(); +} + +/*! + * This function returns a list of objectpaths + * \return TQValueList + * \retval TQValueList + */ +ObjectManagerImpl::DeviceList ObjectManagerImpl::getDevices() +{ + return devices.keys(); +} + +ObjectManagerImpl::ConnectionList ObjectManagerImpl::listConnections(const TQString &adapter) +{ + ConnectionList list; + return list; +} + +bool ObjectManagerImpl::registerAgent() +{ + if (!agentRegisteredStatus) + { + TQT_DBusError error; + agentManager->RegisterAgent( + TQT_DBusObjectPath(TQCString(DBUS_AUTH_SERVICE_PATH)), DEVICE_PIN_CAPABILITY, error); + if (error.isValid()) + { + tqDebug("Could not register agent: %s", error.message().local8Bit().data()); + return false; + } + agentRegisteredStatus = true; + } + return true; +} + +bool ObjectManagerImpl::unregisterAgent() +{ + kdDebug() << k_funcinfo << endl; + if (agentRegisteredStatus) + { + TQT_DBusError error; + getAgentManager()->UnregisterAgent( + TQT_DBusObjectPath(TQCString(DBUS_AUTH_SERVICE_PATH)), error); + if (error.isValid()) + { + tqDebug("Could not unregister agent"); + return false; + } + agentRegisteredStatus = false; + agentIsDefaultAgent = false; + } + return true; +} + +bool ObjectManagerImpl::requestDefaultAgent() +{ + TQT_DBusError error; + agentManager->RequestDefaultAgent( + TQT_DBusObjectPath(TQCString(DBUS_AUTH_SERVICE_PATH)), error); + if (error.isValid()) + { + tqDebug("Could not request default agent: %s", error.message().local8Bit().data()); + return false; + } + agentIsDefaultAgent = true; + return true; +} + +bool ObjectManagerImpl::isAgentRegistered() +{ + return agentRegisteredStatus; +} + +bool ObjectManagerImpl::isAgentDefaultAgent() +{ + return agentIsDefaultAgent; +} + +/*! + * This function initializes the connection to the D-Bus daemon. + * \return boolean with the result of the operation + * \retval true if successful initialized D-Bus connection + * \retval false if unsuccessful + */ +bool ObjectManagerImpl::initDBUS() +{ + dBusConn = TQT_DBusConnection::addConnection(TQT_DBusConnection::SystemBus, DBUS_CONN_NAME); + if (!dBusConn.isConnected()) + { + tqDebug("Failed to open connection to system message bus: %s", dBusConn.lastError().message().local8Bit().data()); + TQTimer::singleShot(4000, this, TQT_SLOT(reconnect())); + return false; + } + setConnection(dBusConn); + + TQT_DBusDataMap objects; + TQT_DBusError error; + if (!GetManagedObjects(objects, error)) + { + tqDebug("GetManagedObjects(objects,error) FAILED:\n%s\n", error.message().latin1()); + return false; + } + + TQT_DBusDataMap::const_iterator it = objects.begin(); + for (it; it != objects.end(); ++it) + { + bool ok = false; + slotInterfacesAdded(it.key(), it.data().toStringKeyMap(&ok)); + if (!ok) + tqWarning("Failed to convert dbus data to string map: %s", it.key().latin1()); + } + + connect(this, SIGNAL(InterfacesAdded(const TQT_DBusObjectPath&, const TQT_DBusDataMap< TQString >&)), + this, SLOT(slotInterfacesAdded(const TQT_DBusObjectPath&, const TQT_DBusDataMap< TQString >& ))); + connect(this, SIGNAL(InterfacesRemoved(const TQT_DBusObjectPath& , const TQStringList& )), + this, SLOT(slotInterfacesRemoved(const TQT_DBusObjectPath& , const TQStringList& ))); + + return true; +} + +void ObjectManagerImpl::adapterPropertiesChanged(TQString path, const TQMap< + TQString, TQT_DBusVariant>& changed_properties) +{ + TQMap::const_iterator it; + for (it = changed_properties.begin(); it != changed_properties.end(); ++it) + { + bool ok = false; + if (it.key() == "Powered") + emit adapterPowerOnChanged(path, it.data().value.toBool(&ok)); + else if (it.key() == "Class") + emit adapterClassChanged(path, it.data().value.toUInt32(&ok)); + else if (it.key() == "Name") + emit adapterNameChanged(path, it.data().value.toString(&ok)); + else if (it.key() == "Alias") + emit adapterAliasChanged(path, it.data().value.toString(&ok)); + else if (it.key() == "DiscoverableTimeout") + emit adapterDiscoverableTimeoutChanged(path, it.data().value.toUInt32(&ok)); + else if (it.key() == "Discoverable") + emit adapterDiscoverableChanged(path, it.data().value.toBool(&ok)); + else if (it.key() == "Discovering") + emit adapterDiscoveringChanged(path, it.data().value.toBool(&ok)); + else + continue; + if (!ok) + tqDebug("ObjectManagerImpl::adapterPropertiesChanged conversion failed"); + } +} + +void ObjectManagerImpl::devicePropertiesChanged(TQString path, const TQMap& changed_properties) +{ + // https://github.com/r10r/bluez/blob/master/doc/device-api.txt + TQMap::const_iterator it; + for (it = changed_properties.begin(); it != changed_properties.end(); ++it) + { + bool ok = false; + if (it.key() == "Address") + emit deviceAddressChanged(path, it.data().value.toString(&ok)); + else if (it.key() == "Class") + emit deviceClassChanged(path, it.data().value.toUInt32(&ok)); + else if (it.key() == "Name") + emit deviceNameChanged(path, it.data().value.toString(&ok)); + else if (it.key() == "Alias") + emit deviceAliasChanged(path, it.data().value.toString(&ok)); +// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml + else if (it.key() == "Appearance") + emit deviceAppearanceChanged(path, it.data().value.toUInt16(&ok)); + else if (it.key() == "Icon") + emit deviceIconChanged(path, it.data().value.toString(&ok)); + else if (it.key() == "Paired") + emit devicePairedChanged(path, it.data().value.toBool(&ok)); + else if (it.key() == "Trusted") + emit deviceTrustedChanged(path, it.data().value.toBool(&ok)); + else if (it.key() == "Blocked") + emit deviceBlockedChanged(path, it.data().value.toBool(&ok)); + else if (it.key() == "LegacyPairing") + emit deviceLegacyPairingChanged(path, it.data().value.toBool(&ok)); + else if (it.key() == "RSSI") + emit deviceRSSIChanged(path, it.data().value.toInt16(&ok)); //INT16 + else if (it.key() == "Connected") + emit deviceConnectedChanged(path, it.data().value.toBool(&ok)); + else if (it.key() == "UUIDs") + { + TQT_DBusDataList vl = TQT_DBusDataList(it.data().value.toTQValueList(&ok)); + emit deviceUUIDsChanged(path, vl.toStringList(&ok)); + } + else if (it.key() == "Adapter") + emit deviceAdapterChanged(path, it.data().value.toObjectPath(&ok)); + else if (it.key() == "ManufacturerData") + emit deviceManufacturerDataChanged(path, it.data().value.toUInt16KeyMap(&ok)); //a{qv} + else if (it.key() == "ServiceData") + emit deviceServiceDataChanged(path, it.data().value.toStringKeyMap(&ok)); //a{sv} + else if (it.key() == "TxPower") + emit deviceTxPowerChanged(path, it.data().value.toInt16(&ok)); //INT16 + else if (it.key() == "ServicesResolved") + emit deviceServicesResolvedChanged(path, it.data().value.toBool(&ok)); + else + continue; + if (!ok) + tqDebug("ObjectManagerImpl::devicePropertiesChanged conversion failed"); + } + +} + +void ObjectManagerImpl::mediaControlPropertiesChanged(TQString path, const TQMap& changed_properties) +{ + TQMap::const_iterator it; + for (it = changed_properties.begin(); it != changed_properties.end(); ++it) + { + bool ok = false; + if (it.key() == "Connected") + emit mediaControlConnectedChanged(path, it.data().value.toBool(&ok)); + else if (it.key() == "Player") + emit mediaControlPlayerChanged(path, it.data().value.toObjectPath(&ok)); + else + continue; + if (!ok) + tqDebug("ObjectManagerImpl::mediaControlPropertiesChanged conversion failed"); + } +} + +void ObjectManagerImpl::slotInterfacesAdded(const TQT_DBusObjectPath& object, const TQT_DBusDataMap& interfaces) +{ + TQT_DBusDataMap::const_iterator it1 = interfaces.begin(); + for (it1; it1 != interfaces.end(); it1++) + { + TQString interface = it1.key(); + if (interface == "org.bluez.AgentManager1") + { + agentManager = new AgentManager1Proxy("org.bluez", object/*, this, "AgentManager1"*/); + if (agentManager) + agentManager->setConnection(dBusConn); + } + else if (interface == "org.bluez.ProfileManager1") + { + profileManager = new ProfileManager1Proxy("org.bluez", object/*, this, "ProfileManager1"*/); + if (profileManager) + profileManager->setConnection(dBusConn); + } + else if (interface == "org.bluez.HealthManager1") + { + healthManager = new HealthManager1Proxy("org.bluez", object/*, this, "HealthManager1"*/); + if (healthManager) + healthManager->setConnection(dBusConn); + } + else if (interface == "org.bluez.Adapter1") + { + org::freedesktop::DBus::PropertiesProxy *properties; + properties = new org::freedesktop::DBus::PropertiesProxy("org.bluez", object); + properties->setConnection(dBusConn); + connect(properties, SIGNAL(PropertiesChanged ( const TQString&, const TQMap< TQString, TQT_DBusVariant >&, const TQStringList& )), this, SLOT(slotPropertiesChanged ( const TQString& , const TQMap< TQString, TQT_DBusVariant >&, const TQStringList& ))); + adapters.insert(TQString(object), properties); + //notify others + emit adapterAdded(TQString(object)); + } + else if (interface == "org.bluez.GattManager1") + { + kdDebug() << "Interface not implemented: org.bluez.GattManager1" << endl; + // TODO: Implement GattManager1 + } + else if (interface == "org.bluez.Media1") + { + kdDebug() << "Interface not implemented: org.bluez.Media1" << endl; + // TODO: Implement Media1 + } + else if (interface == "org.bluez.NetworkServer1") + { + kdDebug() << "Interface not implemented: org.bluez.NetworkServer1" << endl; + // TODO: Implement NetworkServer1 + } + else if (interface == "org.bluez.Device1") + { + org::freedesktop::DBus::PropertiesProxy *properties; + properties = new org::freedesktop::DBus::PropertiesProxy("org.bluez", object); + properties->setConnection(dBusConn); + connect(properties, SIGNAL(PropertiesChanged ( const TQString&, const TQMap< TQString, TQT_DBusVariant >&, const TQStringList& )), this, SLOT(slotPropertiesChanged ( const TQString& , const TQMap< TQString, TQT_DBusVariant >&, const TQStringList& ))); + devices.insert(TQString(object), properties); + //notify others + emit deviceAdded(TQString(object)); + } + else if (interface == "org.bluez.MediaControl1") + { + kdDebug() << "Interface not implemented: org.bluez.MediaControl1" << endl; + kdDebug() << "as the media control is triggered via properties changed." << endl; + } + else if (interface == "org.bluez.MediaTransport1") + { + kdDebug() << "Interface not implemented: org.bluez.MediaTransport1" << endl; + // TODO: Implement MediaTransport1 + } + else if (interface == "org.freedesktop.DBus.Introspectable") + { + // do nothing + } + else if (interface == "org.freedesktop.DBus.Properties") + { + // do nothing + } + else + { + tqWarning("Interface not implemented: %s", interface.local8Bit().data()); + } + } +} + +void ObjectManagerImpl::slotInterfacesRemoved(const TQT_DBusObjectPath& object, const TQStringList& interfaces) +{ + // TODO: remove interface + for (TQValueListConstIterator it = interfaces.begin(); + it != interfaces.end(); ++it) + { + if ((*it) == "org.bluez.AgentManager1") + { + kdDebug() << "Remove org.bluez.AgentManager1" << endl; + // TODO: remove AgentManager1 + } + else if ((*it) == "org.bluez.ProfileManager1") + { + kdDebug() << "Interface not implemented: org.bluez.ProfileManager1" << endl; + // TODO: remove ProfileManager1 + } + else if ((*it) == "org.bluez.HealthManager1") + { + kdDebug() << "Interface not implemented: org.bluez.HealthManager1" << endl; + // TODO: remove HealthManager1 + } + else if ((*it) == "org.bluez.Adapter1") + { + kdDebug() << "Remove org.bluez.Adapter1" << endl; + disconnect(adapters[object], SIGNAL(PropertiesChanged ( const TQString&, const TQMap< TQString, TQT_DBusVariant >&, const TQStringList& )), this, SLOT(slotPropertiesChanged ( const TQString& , const TQMap< TQString, TQT_DBusVariant >&, const TQStringList& ))); + adapters.remove(object); + emit adapterRemoved(TQString(object)); + } + else if ((*it) == "org.bluez.GattManager1") + { + kdDebug() << "Interface not implemented: org.bluez.GattManager1" << endl; + // TODO: Implement GattManager1 + } + else if ((*it) == "org.bluez.Media1") + { + kdDebug() << "Interface not implemented: org.bluez.Media1" << endl; + // TODO: Implement Media1 + } + else if ((*it) == "org.bluez.NetworkServer1") + { + kdDebug() << "Interface not implemented: org.bluez.NetworkServer1" << endl; + // TODO: Implement NetworkServer1 + } + else if ((*it) == "org.bluez.Device1") + { + kdDebug() << "Remove org.bluez.Device1" << endl; + disconnect(devices[object], SIGNAL(PropertiesChanged ( const TQString&, const TQMap< TQString, TQT_DBusVariant >&, const TQStringList& )), this, SLOT(slotPropertiesChanged ( const TQString& , const TQMap< TQString, TQT_DBusVariant >&, const TQStringList& ))); + devices.remove(object); + emit deviceRemoved(TQString(object)); + } + else if ((*it) == "org.bluez.MediaControl1") + { + kdDebug() << "Interface not implemented: org.bluez.MediaControl1" << endl; + kdDebug() << "as the media control is triggered via properties changed." << endl; + // emit mediaControlRemoved(TQString ( object.data() )); + } + else if ((*it) == "org.freedesktop.DBus.Introspectable") + { + // do nothing + } + else if ((*it) == "org.freedesktop.DBus.Properties") + { + // do nothing + } + else + { + tqWarning("Interface not implemented: %s", (*it).local8Bit().data()); + } + } +} + +void ObjectManagerImpl::slotPropertiesChanged(const TQString& interface, const TQMap& changed_properties, const TQStringList& invalidated_properties) +{ + // who send the signal ? + const TQObject * o = TQObject::sender(); + org::freedesktop::DBus::PropertiesProxy *obj; + obj = const_cast(reinterpret_cast(o)); + TQString path; + + if (interface == "org.bluez.Adapter1") + { + for (PropertiesMap::Iterator it = adapters.begin(); + it != adapters.end(); ++it) + { + if (obj == it.data()) + path = it.key(); + } + if (!path.isEmpty()) + adapterPropertiesChanged(path, changed_properties); + } + else if (interface == "org.bluez.Device1") + { + for (PropertiesMap::Iterator it = devices.begin(); it != devices.end(); + ++it) + { + if (obj == it.data()) + path = it.key(); + } + if (!path.isEmpty()) + devicePropertiesChanged(path, changed_properties); + } + else if (interface == "org.bluez.MediaControl1") + { + for (PropertiesMap::Iterator it = devices.begin(); it != devices.end(); + ++it) + { + if (obj == it.data()) + path = it.key(); + } + if (!path.isEmpty()) + mediaControlPropertiesChanged(path, changed_properties); + } + +// TQStringList::const_iterator it1; +// for ( it1 = invalidated_properties.begin(); it1 != invalidated_properties.end(); ++it1 ) +// { +// kdDebug() << "Invalidated Key: " << (*it1) << endl; +//// if ( it.key() == "Powered" ) +//// emit powerOnChanged(TQT_DBusData::fromVariant ( it.data() ).toBool()); +//// if ( it.key() == "DiscoverableTimeout" ) +//// emit discoverableTimeoutChanged(TQT_DBusData::fromVariant ( it.data() ).toUInt32()); +//// if ( it.key() == "Discoverable" ) +//// emit discoverableTimeoutChanged(TQT_DBusData::fromVariant ( it.data() ).toBool()); +// } + +} + +}; // namespace TDEBluetooth + +#include "objectmanagerImpl.moc" +// End of File + diff --git a/src/libtdebluez/objectmanagerImpl.h b/src/libtdebluez/objectmanagerImpl.h new file mode 100644 index 0000000..f8f9c2f --- /dev/null +++ b/src/libtdebluez/objectmanagerImpl.h @@ -0,0 +1,177 @@ +/* + * + * Object Manager implementation of bluez5 + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of libtdebluez. + * + * libtdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * libtdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef OBJECTMANAGERIMPL_H_INCLUDED +#define OBJECTMANAGERIMPL_H_INCLUDED + +#include +#include + +#include "interfaces/objectmanagerProxy.h" +#include "interfaces/agentmanager1Proxy.h" +#include "interfaces/profilemanager1Proxy.h" +#include "interfaces/healthmanager1Proxy.h" +#include "interfaces/propertiesProxy.h" + +#include "adapterImpl.h" +#include "deviceImpl.h" + +//using namespace org::bluez; + +#define DBUS_CONN_NAME "TDEBluez" + +#define DBUS_AUTH_SERVICE_PATH "/org/trinitydesktop/tdebluez" +#define DEVICE_PIN_CAPABILITY "KeyboardDisplay" + +namespace TDEBluetooth +{ + +class ObjectManagerImpl : public org::freedesktop::DBus::ObjectManagerProxy +{ + Q_OBJECT + +public: + ObjectManagerImpl(const TQString& service, const TQString& path, TQObject* parent = 0, const char* name = 0); + + virtual ~ObjectManagerImpl(); + typedef TQMap PropertiesMap; + typedef TQValueList AdapterList; + typedef TQValueList DeviceList; + typedef TQValueList ConnectionList; + // typedef TQValueList ServiceList; + + // --- helper to get private members of the class --- // + //! to get information if TDEBluez is connected to D-Bus + bool isConnectedToDBUS(); + //! pointer to the D-Bus connection + TQT_DBusConnection* getConnection(); + //! to close the connection to D-Bus + bool close(); + + // + AgentManager1Proxy* getAgentManager(); + ProfileManager1Proxy* getProfileManager(); + HealthManager1Proxy* getHealthManager(); + AdapterList getAdapters(); + DeviceList getDevices(); + // ServiceList getServices(); + ConnectionList listConnections(const TQString&); + + + //! to register the agent to D-Bus + bool registerAgent(); //TQT_DBusError& + //! to unregister the agent to D-Bus + bool unregisterAgent(); //TQT_DBusError& + //! to register the agent to D-Bus + bool requestDefaultAgent(); //TQT_DBusError& + + bool isAgentRegistered(); + + bool isAgentDefaultAgent(); + +private: + bool initDBUS(); + void adapterPropertiesChanged(TQString path, const TQMap& changed_properties); + void devicePropertiesChanged(TQString path, const TQMap& changed_properties); + void mediaControlPropertiesChanged(TQString path, const TQMap& changed_properties); + +private: + //! TQt connection to D-Bus + TQT_DBusConnection dBusConn; + AgentManager1Proxy* agentManager; + ProfileManager1Proxy* profileManager; + HealthManager1Proxy* healthManager; + PropertiesMap adapters; + PropertiesMap devices; + bool agentRegisteredStatus; + bool agentIsDefaultAgent; + +signals: + // from ObjectManager + void adapterAdded(const TQString&); + void adapterRemoved(const TQString&); + + void deviceAdded(const TQString&); + void deviceRemoved(const TQString&); + + void mediaControlAdded(const TQString&); + void mediaControlRemoved(const TQString&); + + // from Adapter1 + void adapterNameChanged(const TQString&, const TQString&); + // void adapterModeChanged(const TQString&, const TQString&); + void adapterAliasChanged(const TQString&, const TQString&); + void adapterPowerOnChanged(const TQString&, bool state); + void adapterClassChanged(const TQString&, TQ_UINT32 classvalue); + void adapterDiscoverableTimeoutChanged(const TQString&, TQ_UINT32 timeout); + // TODO: this should be same as modeChanged + void adapterDiscoverableChanged(const TQString&, bool state); + void adapterDiscoveringChanged(const TQString&, bool state); + + // from Device1 + void deviceAddressChanged(const TQString&, const TQString&); + void deviceClassChanged(const TQString&, TQ_UINT32); + void deviceNameChanged(const TQString&, const TQString&); + void deviceAliasChanged(const TQString&, const TQString&); + // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml + void deviceAppearanceChanged(const TQString&, TQ_UINT32); + void deviceIconChanged(const TQString&, const TQString&); + void devicePairedChanged(const TQString&, bool); + void deviceTrustedChanged(const TQString&, bool); + void deviceBlockedChanged(const TQString&, bool); + void deviceLegacyPairingChanged(const TQString&, bool); + void deviceRSSIChanged(const TQString&, TQ_INT16); + void deviceConnectedChanged(const TQString&, bool); + void deviceUUIDsChanged(const TQString&, TQStringList); + void deviceAdapterChanged(const TQString&, const TQT_DBusObjectPath&); + void deviceManufacturerDataChanged(const TQString&, TQT_DBusDataMap); + void deviceServiceDataChanged(const TQString&, TQT_DBusDataMap); + void deviceTxPowerChanged(const TQString&, TQ_INT16); + void deviceServicesResolvedChanged(const TQString&, bool); + + // from MediaControl1 + void mediaControlConnectedChanged(const TQString&, bool state); + void mediaControlPlayerChanged(const TQString&, const TQT_DBusObjectPath&); + +private slots: + bool reconnect(); + + // inherited from ObjectManager + void slotInterfacesAdded(const TQT_DBusObjectPath& object, const TQT_DBusDataMap< TQString >& interfaces); + void slotInterfacesRemoved(const TQT_DBusObjectPath& object, const TQStringList& interfaces); + + /** + * parse properties changed on any interface + * emit signal for the interface and property + */ + void slotPropertiesChanged(const TQString& interface, const TQMap< TQString, TQT_DBusVariant >& changed_properties, const TQStringList& invalidated_properties); + +}; // class ObjectManagerImpl + +}; // namespace TDEBluetooth + +#endif //OBJECTMANAGERIMPL_H_INCLUDED + +// End of File diff --git a/src/libtdeobex/CMakeLists.txt b/src/libtdeobex/CMakeLists.txt new file mode 100644 index 0000000..c998ff0 --- /dev/null +++ b/src/libtdeobex/CMakeLists.txt @@ -0,0 +1,66 @@ +################################################# +# +# (C) 2018 Emanoil Kotsev +# deloptes (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +project( libtdeobex ) +set(LIBRARY_VERSION 0.0.1) + +# include( ConfigureChecks.cmake ) +foreach( f ${TQT_LIBRARIES} ) + if( ${f} STREQUAL "tqt-mt" ) + set(TQUI_LIBRARIES "tqui" CACHE TYPE STRING FORCE) + endif() + if( ${f} STREQUAL "qt-mt" ) + set(TQUI_LIBRARIES "qui" CACHE TYPE STRING FORCE) + endif() +endforeach() + +# import required +#tde_import( lib... ) + +add_subdirectory( interfaces ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR} + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} + ${DBUS_INCLUDE_DIRS} + ${DBUS_TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + +##### headers ################################### +# implementations +install( + FILES obexobjectmanagerImpl.h + DESTINATION ${INCLUDE_INSTALL_DIR}/tdeobex ) + +set( target tdeobex ) + +set( ${target}_SRCS obexobjectmanagerImpl.cpp ) + + +##### tdeobex (shared) ########################### +# set( KDE3_DCOPIDL_EXECUTABLE ${KDE3_DCOPIDLNG_EXECUTABLE} ) +tde_add_library( ${target} SHARED AUTOMOC + SOURCES ${${target}_SRCS} + VERSION ${LIBRARY_VERSION} + DEPENDS obexinterfaces-static + LINK ${DBUS_TQT_LIBRARIES} tdeparts-shared obexinterfaces-static ${TQUI_LIBRARIES} + DESTINATION ${LIB_INSTALL_DIR} + ) + +##### install import cmake modules ############### +tde_install_export( ) diff --git a/src/libtdeobex/interfaces/CMakeLists.txt b/src/libtdeobex/interfaces/CMakeLists.txt new file mode 100644 index 0000000..60a9659 --- /dev/null +++ b/src/libtdeobex/interfaces/CMakeLists.txt @@ -0,0 +1,103 @@ +################################################# +# +# (C) 2020 Emanoil Kotsev +# deloptes (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${TQT_INCLUDE_DIRS} + ${DBUS_TQT_INCLUDE_DIRS} +) + +set( INTROSPECTIONPATH ${CMAKE_SOURCE_DIR}/src/interfaces ) +set( DBUSXML2QT3_EXECUTABLE dbusxml2qt3 ) + +function( make_moc fileinput ) + add_custom_command( OUTPUT ${fileinput}.moc + COMMAND ${TMOC_EXECUTABLE} ${fileinput}.h -o ${fileinput}.moc + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${fileinput}.h + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_property( SOURCE ${CMAKE_CURRENT_BINARY_DIR}/${fileinput}.cpp APPEND + PROPERTY OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${fileinput}.moc ) +endfunction( ) + +set( ObjectManager_HDRS objectmanagerInterface.h objectmanagerProxy.h introspectableInterface.h ) +set( ObjectManager_SRCS objectmanagerInterface.cpp objectmanagerProxy.cpp introspectableInterface.cpp ) + +set (Agent_HDRS agent1Interface.h agent1Proxy.h dbusbaseNode.h tdeobexNode.h) +set (Agent_SRCS agent1Interface.cpp agent1Proxy.cpp dbusbaseNode.cpp tdeobexNode.cpp) + +set (Client_HDRS agentmanager1Interface.h agentmanager1Proxy.h client1Interface.h client1Proxy.h filetransfer1Interface.h filetransfer1Proxy.h obexNode.h objectpush1Interface.h objectpush1Proxy.h phonebookaccess1Interface.h phonebookaccess1Proxy.h session1Interface.h session1Proxy.h synchronization1Interface.h synchronization1Proxy.h transfer1Interface.h transfer1Proxy.h) +set (Client_SRCS agentmanager1Interface.cpp agentmanager1Proxy.cpp client1Interface.cpp client1Proxy.cpp filetransfer1Interface.cpp filetransfer1Proxy.cpp obexNode.cpp objectpush1Interface.cpp objectpush1Proxy.cpp phonebookaccess1Interface.cpp phonebookaccess1Proxy.cpp session1Interface.cpp session1Proxy.cpp synchronization1Interface.cpp synchronization1Proxy.cpp transfer1Interface.cpp transfer1Proxy.cpp propertiesProxy.h propertiesProxy.cpp ) + + +##### ObjectManager ######################### +add_custom_command( + OUTPUT ${ObjectManager_HDRS} ${ObjectManager_SRCS} + COMMAND ${DBUSXML2QT3_EXECUTABLE} ${INTROSPECTIONPATH}/org.freedesktop.DBus.ObjectManager.xml 2>/dev/null + DEPENDS ${INTROSPECTIONPATH}/org.freedesktop.DBus.ObjectManager.xml + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) +make_moc ( objectmanagerProxy ) + +##### Agent ######################### +add_custom_command( + OUTPUT ${Agent_HDRS} ${Agent_SRCS} + COMMAND ${DBUSXML2QT3_EXECUTABLE} ${INTROSPECTIONPATH}/org.bluez.obex.Agent1.xml 2>/dev/null + DEPENDS ${INTROSPECTIONPATH}/org.bluez.obex.Agent1.xml + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) +make_moc ( agent1Proxy ) + +##### Client ######################### +add_custom_command( + OUTPUT ${Client_HDRS} ${Client_SRCS} + COMMAND ${DBUSXML2QT3_EXECUTABLE} ${INTROSPECTIONPATH}/org.bluez.obex.client.xml 2>/dev/null + DEPENDS ${INTROSPECTIONPATH}/org.bluez.obex.client.xml + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) +make_moc ( agentmanager1Proxy ) +make_moc ( client1Proxy ) +make_moc ( filetransfer1Proxy ) +make_moc ( objectpush1Proxy ) +make_moc ( phonebookaccess1Proxy ) +make_moc ( session1Proxy ) +make_moc ( synchronization1Proxy ) +make_moc ( transfer1Proxy ) +make_moc ( propertiesProxy ) + +tde_add_library( obexinterfaces STATIC_PIC AUTOMOC + SOURCES ${ObjectManager_SRCS} ${Agent_SRCS} ${Client_SRCS} + LINK ${DBUS_TQT_LIBRARIES} +) + +##### headers ################################### + +#file( GLOB _bin_dirs RELATIVE ${CMAKE_CURRENT_BINARY_DIR} +#      ${CMAKE_CURRENT_BINARY_DIR}/* ) +#unset( _exclude_dirs ) +#foreach( _dir IN LISTS _bin_dirs ) +#  if( IS_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${_dir} ) +#    list( APPEND _exclude_dirs PATTERN ${_dir} EXCLUDE ) +#  endif() +#endforeach() +# +#install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +# DESTINATION ${INCLUDE_INSTALL_DIR}/tdeobex +# USE_SOURCE_PERMISSIONS +# FILES_MATCHING PATTERN PATTERN "*.h" +# ${_exclude_dirs} +#) + +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DESTINATION ${INCLUDE_INSTALL_DIR}/tdeobex + USE_SOURCE_PERMISSIONS + FILES_MATCHING PATTERN PATTERN "*.h" + PATTERN "CMakeFiles" EXCLUDE ) diff --git a/src/libtdeobex/obexobjectmanagerImpl.cpp b/src/libtdeobex/obexobjectmanagerImpl.cpp new file mode 100644 index 0000000..56a8798 --- /dev/null +++ b/src/libtdeobex/obexobjectmanagerImpl.cpp @@ -0,0 +1,286 @@ +/* + * + * Object Manager implementation of bluez5 + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of libtdebluez. + * + * libtdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * libtdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +// QT - Header +#include +// +// debug +#include + +// declaration include +#include +#include +#include +#include +#include +// +#include + +#include "obexobjectmanagerImpl.h" +#include "../libtdebluez/btuuids.h" + +namespace TDEObex +{ + +ObexObjectManagerImpl::ObexObjectManagerImpl(const TQString& service, const TQString& path, TQObject* parent, const char* name) : + org::freedesktop::DBus::ObjectManagerProxy(service, path, parent, name) +{ + kdDebug() << k_funcinfo << endl; + // init connection to dbus + initDBUS(); +} + +ObexObjectManagerImpl::~ObexObjectManagerImpl() +{ + kdDebug() << k_funcinfo << endl; + close(); +} + +/*! + * This function try a reconnect to D-Bus. + * \return boolean with the result of the operation + * \retval true if successful reconnected to D-Bus + * \retval false if unsuccessful + */ +bool ObexObjectManagerImpl::reconnect() +{ + kdDebug() << k_funcinfo << endl; + // close D-Bus connection + close(); + // init D-Bus conntection + return (initDBUS()); +} + +/*! + * This function return information about connection status to the DBUS daemon. + * \return boolean with the state of the connection to D-Bus + * \retval true if connected + * \retval false if disconnected + */ +bool ObexObjectManagerImpl::isConnectedToDBUS() +{ + kdDebug() << k_funcinfo << endl; + return dBusConn.isConnected(); +} + +/*! + * This function returns pointer to connection of the DBUS. + * \return TQT_DBusConnection* of the connection to D-Bus + * \retval TQT_DBusConnection* + */ +TQT_DBusConnection* ObexObjectManagerImpl::getConnection() +{ + kdDebug() << k_funcinfo << endl; + return &dBusConn; +} + +/*! + * This function close the connection to manager over the D-Bus daemon. + * \return boolean with the result of the operation + * \retval true if successful closed the connection + * \retval false if any problems + */ +bool ObexObjectManagerImpl::close() +{ + kdDebug() << k_funcinfo << endl; + + if (mSession) + delete mSession; + if (mFileTransfer) + delete mFileTransfer; + if (mClient) + delete mClient; + if (dBusConn.isConnected()) + dBusConn.closeConnection(DBUS_CONN_NAME); + return true; +} + +/*! + * This function initialise the connection to the D-Bus daemon. + * \return boolean with the result of the operation + * \retval true if successful initialised D-Bus connection + * \retval false if unsuccessful + */ +bool ObexObjectManagerImpl::initDBUS() +{ + kdDebug() << k_funcinfo << endl; + dBusConn = TQT_DBusConnection::addConnection(TQT_DBusConnection::SessionBus, DBUS_CONN_NAME); + if (!dBusConn.isConnected()) + { + kdError() << "Failed to open connection to system message bus: " << dBusConn.lastError().message() << endl; + TQTimer::singleShot(4000, this, TQT_SLOT(reconnect())); + return false; + } + setConnection(dBusConn); + + TQT_DBusDataMap objects; + TQT_DBusError error; + if (!GetManagedObjects(objects, error)) + { + tqDebug("GetManagedObjects(objects,error) FAILED\n"); + return false; + } + + TQT_DBusDataMap::const_iterator it = objects.begin(); + for (it; it != objects.end(); ++it) + { + bool ok = false; + TQT_DBusDataMap tqMap1 = it.data().toStringKeyMap(&ok); + if (!ok) + { + tqWarning("Failed to convert dbus data to string map: %s", it.key().latin1()); + return false; + } + slotInterfacesAdded(it.key(), tqMap1); + } + + connect(this, SIGNAL(InterfacesAdded(const TQT_DBusObjectPath&, const TQT_DBusDataMap< TQString >&)), this, SLOT(slotInterfacesAdded(const TQT_DBusObjectPath&, const TQT_DBusDataMap< TQString >& ))); + connect(this, SIGNAL(InterfacesRemoved(const TQT_DBusObjectPath& , const TQStringList& )), this, SLOT(slotInterfacesRemoved(const TQT_DBusObjectPath& , const TQStringList& ))); + return true; +} + +/*! + * This function initialise the connection to the D-Bus daemon. + * \return pointer to AgentManager1 + */ +org::bluez::obex::AgentManager1Proxy * ObexObjectManagerImpl::getAgentManager() +{ + kdDebug() << k_funcinfo << endl; + return mAgentManager; +} + +/*! + * This function initialise the connection to the D-Bus daemon. + * \return pointer to AgentManager1 + */ +org::bluez::obex::Client1Proxy * ObexObjectManagerImpl::getClient() +{ + kdDebug() << k_funcinfo << endl; + return mClient; +} + +void ObexObjectManagerImpl::slotInterfacesAdded(const TQT_DBusObjectPath& object, const TQT_DBusDataMap& interfaces) +{ + kdDebug() << k_funcinfo << endl; + + TQT_DBusDataMap::const_iterator it1 = interfaces.begin(); + for (it1; it1 != interfaces.end(); it1++) + { + TQString interface = it1.key(); + if (interface == "org.bluez.obex.AgentManager1") + { + mAgentManager = new org::bluez::obex::AgentManager1Proxy("org.bluez.obex", object); + if (mAgentManager) + { + mAgentManager->setConnection(dBusConn); + } + else + { + tqDebug("org.bluez.obex.AgentManager1 initialization failed\n"); + } + } + else if (interface == "org.bluez.obex.Client1") + { + mClient = new org::bluez::obex::Client1Proxy("org.bluez.obex", object); + if (mClient) + { + mClient->setConnection(dBusConn); + } + else + { + tqDebug("org.bluez.obex.Client1 initialization failed\n"); + } + } + else if (interface == "org.bluez.obex.Session1") + { + mSession = new org::bluez::obex::Session1Proxy("org.bluez.obex", object); + if (mSession) + { + mSession->setConnection(dBusConn); + } + else + { + tqDebug("org.bluez.obex.Session1 initialization failed\n"); + } + } + else if (interface == "org.bluez.obex.FileTransfer1") + { + mFileTransfer = new org::bluez::obex::FileTransfer1Proxy("org.bluez.obex", object); + if (mFileTransfer) + { + mFileTransfer->setConnection(dBusConn); + } + else + { + tqDebug("org.bluez.obex.FileTransfer1 initialization failed\n"); + } + } + else if (interface == "org.freedesktop.DBus.Introspectable") + { + // do nothing + } + else + { + tqWarning("Interface not implemented: %s", interface.local8Bit().data()); + } + } +} + +void ObexObjectManagerImpl::slotInterfacesRemoved(const TQT_DBusObjectPath& object, const TQStringList& interfaces) +{ + kdDebug() << k_funcinfo << endl; + // TODO: remove interface + for (TQValueListConstIterator it = interfaces.begin(); + it != interfaces.end(); ++it) + { + if ((*it) == "org.bluez.obex.AgentManager1") + { + // TODO: remove AgentManager1 + } + else if ((*it) == "org.bluez.obex.Client1") + { + // TODO: remove Client1 + } + else if ((*it) == "org.bluez.obex.Session1") + { + // TODO: remove Session1 + } + else if ((*it) == "org.bluez.obex.FileTransfer1") + { + // TODO: remove FileTransfer1 + } + else + { + tqWarning("Interface not implemented: %s", (*it).local8Bit().data()); + } + } +} + +}; +// namespace TDEObex + +#include "obexobjectmanagerImpl.moc" +// End of File + diff --git a/src/libtdeobex/obexobjectmanagerImpl.h b/src/libtdeobex/obexobjectmanagerImpl.h new file mode 100644 index 0000000..65ef978 --- /dev/null +++ b/src/libtdeobex/obexobjectmanagerImpl.h @@ -0,0 +1,94 @@ +/* + * + * Obex Object Manager implementation of bluez5 + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of libtdebluez. + * + * libtdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * libtdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef OBEXOBJECTMANAGERIMPL_H_INCLUDED +#define OBEXOBJECTMANAGERIMPL_H_INCLUDED + +#include +#include + +#include "interfaces/propertiesProxy.h" +#include "interfaces/objectmanagerProxy.h" +#include "interfaces/agentmanager1Proxy.h" +#include "interfaces/client1Proxy.h" +#include "interfaces/session1Proxy.h" +#include "interfaces/filetransfer1Proxy.h" + +namespace TDEObex +{ + +#define DBUS_CONN_NAME "TDEBluezObex" + +class ObexObjectManagerImpl: public org::freedesktop::DBus::ObjectManagerProxy +{ + Q_OBJECT + +public: + // ObexObjectManagerImpl(){} + ObexObjectManagerImpl(const TQString& service, const TQString& path, TQObject* parent = 0, const char* name = 0); + + virtual ~ObexObjectManagerImpl(); + + // --- helper to get private members of the class --- // + //! to get information if TDEBluez is connected to D-Bus + bool isConnectedToDBUS(); + //! pointer to the D-Bus connection + TQT_DBusConnection* getConnection(); + //! to close the connection to D-Bus + bool close(); + + // + org::bluez::obex::AgentManager1Proxy* getAgentManager(); + org::bluez::obex::Client1Proxy* getClient(); + // ConnectionList listConnections(const TQString&); + +private: + bool initDBUS(); + + void slotInterfacesAdded(const TQT_DBusObjectPath& object, const TQT_DBusDataMap< + TQString>& interfaces); + + void slotInterfacesRemoved(const TQT_DBusObjectPath& object, const TQStringList& interfaces); + +private: + //! TQt connection to D-Bus + TQT_DBusConnection dBusConn; + org::bluez::obex::AgentManager1Proxy* mAgentManager; + org::bluez::obex::Client1Proxy* mClient; + org::bluez::obex::Session1Proxy* mSession; + org::bluez::obex::FileTransfer1Proxy* mFileTransfer; + +private slots: + bool reconnect(); + +}; +// class ObexObjectManagerImpl + +}; +// namespace TDEObex + +#endif //OBEXOBJECTMANAGERIMPL_H_INCLUDED + +// End of File diff --git a/src/tdebluez-common/CMakeLists.txt b/src/tdebluez-common/CMakeLists.txt new file mode 100644 index 0000000..8dbf649 --- /dev/null +++ b/src/tdebluez-common/CMakeLists.txt @@ -0,0 +1,67 @@ +################################################# +# +# (C) 2018 Emanoil Kotsev +# deloptes (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +project( tdebluez-common ) + +# include( ConfigureChecks.cmake ) + +# import required +#tde_import( lib... ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/tdebluez-common + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +#link_directories( +# ${TQT_LIBRARY_DIRS} +#) + + +##### other data ################################ +# Example +#install( FILES knotes.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} ) +#install( FILES knoteconfig.kcfg knotesglobalconfig.kcfg DESTINATION ${KCFG_INSTALL_DIR} ) +#install( FILES knotesappui.rc knotesui.rc DESTINATION ${DATA_INSTALL_DIR}/knotes ) +#install( FILES local.desktop DESTINATION ${SERVICES_INSTALL_DIR}/tderesources/knotes ) +#install( FILES knotes_manager.desktop DESTINATION ${SERVICES_INSTALL_DIR}/tderesources ) + +# # from tdebase/applnk/Makefile.am +# install-data-local: +# $(mkinstalldirs) $(DESTDIR)$(kde_appsdir)/Settings/Network/Bluetooth +# $(INSTALL_DATA) $(srcdir)/kde-settings-network-bluetooth.directory \ +# $(DESTDIR)$(kde_appsdir)/Settings/Network/Bluetooth/.directory +# +# uninstall-local: +# -rm -f $(DESTDIR)$(kde_appsdir)/Settings/Network/Bluetooth/.directory + +##### install import cmake modules ############### +#tde_install_export( ) + +# Bluetooth directory entry in the control center +install( FILES tde-settings-network-bluetooth.directory + DESTINATION ${DATA_INSTALL_DIR}/tdebluez ) + +# Bluetooth access to system debus org.bluez +install( FILES org.trinitydesktop.tdebluez.conf + DESTINATION ${DBUS_SYS_DIR} ) +# DESTINATION ${DBUS_SYSTEM_CONF_DIRECTORY} ) + + + +add_subdirectory( icons ) +add_subdirectory( mimetypes ) +#add_subdirectory( dunhandler ) +#add_subdirectory( faxhandler ) \ No newline at end of file diff --git a/src/tdebluez-common/README b/src/tdebluez-common/README new file mode 100644 index 0000000..a397554 --- /dev/null +++ b/src/tdebluez-common/README @@ -0,0 +1,3 @@ +This directory contains some files which are shared +among the various modules in tdebluez. + diff --git a/src/tdebluez-common/dunhandler/CMakeLists.txt b/src/tdebluez-common/dunhandler/CMakeLists.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/tdebluez-common/dunhandler/Makefile.am b/src/tdebluez-common/dunhandler/Makefile.am new file mode 100644 index 0000000..17b1e17 --- /dev/null +++ b/src/tdebluez-common/dunhandler/Makefile.am @@ -0,0 +1,7 @@ + +dunhandlerdir = $(kde_datadir)/kdebluetooth/dunhandler +dunhandler_SCRIPTS=dunhandler + +# The desktop file +servicemenudir = $(kde_appsdir)/Utilities +servicemenu_DATA = dunhandler.desktop \ No newline at end of file diff --git a/src/tdebluez-common/dunhandler/Makefile.in b/src/tdebluez-common/dunhandler/Makefile.in new file mode 100644 index 0000000..cf05434 --- /dev/null +++ b/src/tdebluez-common/dunhandler/Makefile.in @@ -0,0 +1,804 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# KDE tags expanded automatically by am_edit - $Revision$ +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = kdebluetooth/kdebluetooth-common/dunhandler +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(SHELL) $(top_srcdir)/admin/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = dunhandler.desktop +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(dunhandlerdir)" \ + "$(DESTDIR)$(servicemenudir)" +SCRIPTS = $(dunhandler_SCRIPTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(servicemenu_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(srcdir)/dunhandler.desktop.in \ + $(top_srcdir)/admin/mkinstalldirs +#>- DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +#>+ 1 +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) $(KDE_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +ARTSCCONFIG = @ARTSCCONFIG@ +AUTOCONF = @AUTOCONF@ +AUTODIRS = @AUTODIRS@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BLUETOOTH_CFLAGS = @BLUETOOTH_CFLAGS@ +BLUETOOTH_LIBS = @BLUETOOTH_LIBS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CONF_FILES = @CONF_FILES@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DBUSTQT_CFLAGS = @DBUSTQT_CFLAGS@ +DBUSTQT_LIBS = @DBUSTQT_LIBS@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ +DCOPIDL = @DCOPIDL@ +DCOPIDL2CPP = @DCOPIDL2CPP@ +DCOPIDLNG = @DCOPIDLNG@ +DCOP_DEPENDENCIES = @DCOP_DEPENDENCIES@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DOXYGEN_PROJECT_NAME = @DOXYGEN_PROJECT_NAME@ +DOXYGEN_PROJECT_NUMBER = @DOXYGEN_PROJECT_NUMBER@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ENABLE_PERMISSIVE_FLAG = @ENABLE_PERMISSIVE_FLAG@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FRAMEWORK_COREAUDIO = @FRAMEWORK_COREAUDIO@ +GMSGFMT = @GMSGFMT@ +GREP = @GREP@ +HAVE_GCC_VISIBILITY = @HAVE_GCC_VISIBILITY@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KBTOBEXSRV_BINDIR = @KBTOBEXSRV_BINDIR@ +KCFG_DEPENDENCIES = @KCFG_DEPENDENCIES@ +KCONFIG_COMPILER = @KCONFIG_COMPILER@ +KDEBLUETOOTH_VERSION = @KDEBLUETOOTH_VERSION@ +KDECONFIG = @KDECONFIG@ +KDE_CHECK_PLUGIN = @KDE_CHECK_PLUGIN@ +KDE_EXTRA_RPATH = @KDE_EXTRA_RPATH@ +KDE_HAS_DOXYGEN = @KDE_HAS_DOXYGEN@ +KDE_HAVE_DOT = @KDE_HAVE_DOT@ +KDE_INCLUDES = @KDE_INCLUDES@ +KDE_LDFLAGS = @KDE_LDFLAGS@ +KDE_MT_LDFLAGS = @KDE_MT_LDFLAGS@ +KDE_MT_LIBS = @KDE_MT_LIBS@ +KDE_NO_UNDEFINED = @KDE_NO_UNDEFINED@ +KDE_PLUGIN = @KDE_PLUGIN@ +KDE_RPATH = @KDE_RPATH@ +KDE_USE_CLOSURE_FALSE = @KDE_USE_CLOSURE_FALSE@ +KDE_USE_CLOSURE_TRUE = @KDE_USE_CLOSURE_TRUE@ +KDE_USE_FINAL_FALSE = @KDE_USE_FINAL_FALSE@ +KDE_USE_FINAL_TRUE = @KDE_USE_FINAL_TRUE@ +KDE_USE_FPIE = @KDE_USE_FPIE@ +KDE_USE_NMCHECK_FALSE = @KDE_USE_NMCHECK_FALSE@ +KDE_USE_NMCHECK_TRUE = @KDE_USE_NMCHECK_TRUE@ +KDE_USE_PIE = @KDE_USE_PIE@ +KDE_XSL_STYLESHEET = @KDE_XSL_STYLESHEET@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LDFLAGS_AS_NEEDED = @LDFLAGS_AS_NEEDED@ +LDFLAGS_NEW_DTAGS = @LDFLAGS_NEW_DTAGS@ +LIBCOMPAT = @LIBCOMPAT@ +LIBCRYPT = @LIBCRYPT@ +LIBDL = @LIBDL@ +LIBJPEG = @LIBJPEG@ +LIBOBJS = @LIBOBJS@ +LIBPNG = @LIBPNG@ +LIBPTHREAD = @LIBPTHREAD@ +LIBRESOLV = @LIBRESOLV@ +LIBS = @LIBS@ +LIBSM = @LIBSM@ +LIBSOCKET = @LIBSOCKET@ +LIBTOOL = @LIBTOOL@ +LIBTQT_LDFLAGS = @LIBTQT_LDFLAGS@ +LIBUCB = @LIBUCB@ +LIBUTIL = @LIBUTIL@ +LIBXML_CFLAGS = @LIBXML_CFLAGS@ +LIBXML_LIBS = @LIBXML_LIBS@ +LIBZ = @LIBZ@ +LIB_ARTS = @LIB_ARTS@ +LIB_BLUETOOTH = @LIB_BLUETOOTH@ +LIB_KAB = @LIB_KAB@ +LIB_KDED = @LIB_KDED@ +LIB_KFM = @LIB_KFM@ +LIB_KJS = @LIB_KJS@ +LIB_LOCKDEV = @LIB_LOCKDEV@ +LIB_POLL = @LIB_POLL@ +LIB_QPE = @LIB_QPE@ +LIB_QT = @LIB_QT@ +LIB_QUI = @LIB_QUI@ +LIB_SDP = @LIB_SDP@ +LIB_SMB = @LIB_SMB@ +LIB_TDEABC = @LIB_TDEABC@ +LIB_TDECORE = @LIB_TDECORE@ +LIB_TDEDNSSD = @LIB_TDEDNSSD@ +LIB_TDEFILE = @LIB_TDEFILE@ +LIB_TDEHTML = @LIB_TDEHTML@ +LIB_TDEIMPROXY = @LIB_TDEIMPROXY@ +LIB_TDEIO = @LIB_TDEIO@ +LIB_TDENEWSTUFF = @LIB_TDENEWSTUFF@ +LIB_TDEPARTS = @LIB_TDEPARTS@ +LIB_TDEPIM = @LIB_TDEPIM@ +LIB_TDEPRINT = @LIB_TDEPRINT@ +LIB_TDESPELL = @LIB_TDESPELL@ +LIB_TDESYCOCA = @LIB_TDESYCOCA@ +LIB_TDEUI = @LIB_TDEUI@ +LIB_TDEUNITTEST = @LIB_TDEUNITTEST@ +LIB_TDEUTILS = @LIB_TDEUTILS@ +LIB_X11 = @LIB_X11@ +LIB_XEXT = @LIB_XEXT@ +LIB_XRENDER = @LIB_XRENDER@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MAKETDEWIDGETS = @MAKETDEWIDGETS@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MCOPIDL = @MCOPIDL@ +MEINPROC = @MEINPROC@ +MKDIR_P = @MKDIR_P@ +MOC = @MOC@ +MSGFMT = @MSGFMT@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NOOPT_CFLAGS = @NOOPT_CFLAGS@ +NOOPT_CXXFLAGS = @NOOPT_CXXFLAGS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENOBEX_CFLAGS = @OPENOBEX_CFLAGS@ +OPENOBEX_LIBS = @OPENOBEX_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +QTDOCDIR = @QTDOCDIR@ +QTE_NORTTI = @QTE_NORTTI@ +QT_INCLUDES = @QT_INCLUDES@ +QT_LDFLAGS = @QT_LDFLAGS@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TOPSUBDIRS = @TOPSUBDIRS@ +TQTDCOPIDL = @TQTDCOPIDL@ +TQTDCOPIDL2CPP = @TQTDCOPIDL2CPP@ +TQTDCOPIDLNG = @TQTDCOPIDLNG@ +TQTMCOPIDL = @TQTMCOPIDL@ +TQTMOC = @TQTMOC@ +UIC = @UIC@ +UIC_TR = @UIC_TR@ +USER_INCLUDES = @USER_INCLUDES@ +USER_LDFLAGS = @USER_LDFLAGS@ +USE_EXCEPTIONS = @USE_EXCEPTIONS@ +USE_RTTI = @USE_RTTI@ +USE_THREADS = @USE_THREADS@ +VERSION = @VERSION@ +WOVERLOADED_VIRTUAL = @WOVERLOADED_VIRTUAL@ +XGETTEXT = @XGETTEXT@ +XMKMF = @XMKMF@ +XMLLINT = @XMLLINT@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_INCLUDES = @X_INCLUDES@ +X_LDFLAGS = @X_LDFLAGS@ +X_PRE_LIBS = @X_PRE_LIBS@ +X_RPATH = @X_RPATH@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +all_includes = @all_includes@ +all_libraries = @all_libraries@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dunhandler_dir = @dunhandler_dir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +faxhandler_dir = @faxhandler_dir@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +kde_appsdir = @kde_appsdir@ +kde_bindir = @kde_bindir@ +kde_confdir = @kde_confdir@ +kde_datadir = @kde_datadir@ +kde_htmldir = @kde_htmldir@ +kde_icondir = @kde_icondir@ +kde_includes = @kde_includes@ +kde_kcfgdir = @kde_kcfgdir@ +kde_libraries = @kde_libraries@ +kde_libs_htmldir = @kde_libs_htmldir@ +kde_libs_prefix = @kde_libs_prefix@ +kde_locale = @kde_locale@ +kde_mimedir = @kde_mimedir@ +kde_moduledir = @kde_moduledir@ +kde_qtver = @kde_qtver@ +kde_servicesdir = @kde_servicesdir@ +kde_servicetypesdir = @kde_servicetypesdir@ +kde_sounddir = @kde_sounddir@ +kde_styledir = @kde_styledir@ +kde_templatesdir = @kde_templatesdir@ +kde_wallpaperdir = @kde_wallpaperdir@ +kde_widgetdir = @kde_widgetdir@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +qt_includes = @qt_includes@ +qt_libraries = @qt_libraries@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +tdeinitdir = @tdeinitdir@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +x_includes = @x_includes@ +x_libraries = @x_libraries@ +xdg_appsdir = @xdg_appsdir@ +xdg_directorydir = @xdg_directorydir@ +xdg_menudir = @xdg_menudir@ +dunhandlerdir = $(kde_datadir)/kdebluetooth/dunhandler +dunhandler_SCRIPTS = dunhandler + +# The desktop file +servicemenudir = $(kde_appsdir)/Utilities +servicemenu_DATA = dunhandler.desktop +#>- all: all-am +#>+ 1 +all: docs-am all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) +#>- @for dep in $?; do \ +#>- case '$(am__configure_deps)' in \ +#>- *$$dep*) \ +#>- ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ +#>- && { if test -f $@; then exit 0; else break; fi; }; \ +#>- exit 1;; \ +#>- esac; \ +#>- done; \ +#>- echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign kdebluetooth/kdebluetooth-common/dunhandler/Makefile'; \ +#>- $(am__cd) $(top_srcdir) && \ +#>- $(AUTOMAKE) --foreign kdebluetooth/kdebluetooth-common/dunhandler/Makefile +#>+ 12 + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign kdebluetooth/kdebluetooth-common/dunhandler/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign kdebluetooth/kdebluetooth-common/dunhandler/Makefile + cd $(top_srcdir) && perl admin/am_edit kdebluetooth/kdebluetooth-common/dunhandler/Makefile.in +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +dunhandler.desktop: $(top_builddir)/config.status $(srcdir)/dunhandler.desktop.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +install-dunhandlerSCRIPTS: $(dunhandler_SCRIPTS) + @$(NORMAL_INSTALL) + @list='$(dunhandler_SCRIPTS)'; test -n "$(dunhandlerdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(dunhandlerdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(dunhandlerdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(dunhandlerdir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(dunhandlerdir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-dunhandlerSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(dunhandler_SCRIPTS)'; test -n "$(dunhandlerdir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + dir='$(DESTDIR)$(dunhandlerdir)'; $(am__uninstall_files_from_dir) + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-servicemenuDATA: $(servicemenu_DATA) + @$(NORMAL_INSTALL) + @list='$(servicemenu_DATA)'; test -n "$(servicemenudir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(servicemenudir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(servicemenudir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(servicemenudir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(servicemenudir)" || exit $$?; \ + done + +uninstall-servicemenuDATA: + @$(NORMAL_UNINSTALL) + @list='$(servicemenu_DATA)'; test -n "$(servicemenudir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(servicemenudir)'; $(am__uninstall_files_from_dir) +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(SCRIPTS) $(DATA) +installdirs: + for dir in "$(DESTDIR)$(dunhandlerdir)" "$(DESTDIR)$(servicemenudir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +#>- clean: clean-am +#>+ 1 +clean: kde-rpo-clean clean-am + +#>- clean-am: clean-generic clean-libtool mostlyclean-am +#>+ 1 +clean-am: clean-bcheck clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-dunhandlerSCRIPTS install-servicemenuDATA + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-dunhandlerSCRIPTS uninstall-servicemenuDATA + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am \ + install-dunhandlerSCRIPTS install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am \ + install-servicemenuDATA install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ + uninstall-am uninstall-dunhandlerSCRIPTS \ + uninstall-servicemenuDATA + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: + +#>+ 2 +KDE_DIST=dunhandler dunhandler.desktop.in configure.in.in Makefile.in Makefile.am + +#>+ 2 +docs-am: + +#>+ 15 +force-reedit: + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign kdebluetooth/kdebluetooth-common/dunhandler/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign kdebluetooth/kdebluetooth-common/dunhandler/Makefile + cd $(top_srcdir) && perl admin/am_edit kdebluetooth/kdebluetooth-common/dunhandler/Makefile.in + + +#>+ 21 +clean-bcheck: + rm -f *.bchecktest.cc *.bchecktest.cc.class a.out + +bcheck: bcheck-am + +bcheck-am: + @for i in ; do \ + if test $(srcdir)/$$i -nt $$i.bchecktest.cc; then \ + echo "int main() {return 0;}" > $$i.bchecktest.cc ; \ + echo "#include \"$$i\"" >> $$i.bchecktest.cc ; \ + echo "$$i"; \ + if ! $(CXX) $(DEFS) -I. -I$(srcdir) -I$(top_builddir) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(KDE_CXXFLAGS) --dump-class-hierarchy -c $$i.bchecktest.cc; then \ + rm -f $$i.bchecktest.cc; exit 1; \ + fi ; \ + echo "" >> $$i.bchecktest.cc.class; \ + perl $(top_srcdir)/admin/bcheck.pl $$i.bchecktest.cc.class || { rm -f $$i.bchecktest.cc; exit 1; }; \ + rm -f a.out; \ + fi ; \ + done + + +#>+ 3 +final: + $(MAKE) all-am + +#>+ 3 +final-install: + $(MAKE) install-am + +#>+ 3 +no-final: + $(MAKE) all-am + +#>+ 3 +no-final-install: + $(MAKE) install-am + +#>+ 3 +kde-rpo-clean: + -rm -f *.rpo + +#>+ 3 +nmcheck: +nmcheck-am: nmcheck diff --git a/src/tdebluez-common/dunhandler/configure.in.in b/src/tdebluez-common/dunhandler/configure.in.in new file mode 100644 index 0000000..505e06a --- /dev/null +++ b/src/tdebluez-common/dunhandler/configure.in.in @@ -0,0 +1,4 @@ +dnl AC_OUTPUT(kdebluetooth/kdebluetooth-common/dunhandler/dunhandler.desktop) +KDE_EXPAND_MAKEVAR(dunhandler_dir, kde_datadir) +AC_SUBST(dunhandler_dir) + diff --git a/src/tdebluez-common/dunhandler/dunhandler b/src/tdebluez-common/dunhandler/dunhandler new file mode 100644 index 0000000..ab7aa83 --- /dev/null +++ b/src/tdebluez-common/dunhandler/dunhandler @@ -0,0 +1,70 @@ +#!/bin/sh +#/*************************************************************************** +# dunhandler - a script for tdeio_sdp +# ------------------- +# begin : Mon March 29 2004 +# copyright : (C) 2004 by Simone Gotti +# email : simone.gotti@email.it +# ***************************************************************************/ +# +#/*************************************************************************** +# * * +# * This program is free software; you can redistribute it and/or modify * +# * it under the terms of the GNU General Public License as published by * +# * the Free Software Foundation; either version 2 of the License, or * +# * (at your option) any later version. * +# * * +# ***************************************************************************/ + + + +# get the btaddress and the rfcomm channel from the command line +BTADDR=$(echo $1 | cut -d'/' -f3 | cut -d'[' -f2 | cut -d']' -f1) +PARAMS=$(echo $1 | cut -d'?' -f2 ) + +OLDIFS=$IFS +IFS='&' +for i in $PARAMS; do + if test $(echo $i | cut -d'=' -f1) = "rfcommchannel"; then + RFCOMM_CHANNEL=$(echo $i | cut -d'=' -f2) + fi +done; +IFS=$OLDIFS + +RFCOMM_SHOW_OUT=$(rfcomm show) + +# Check if the channel is already binded +BINDED_CHANNEL=$(echo $RFCOMM_SHOW_OUT | grep -i $BTADDR | grep -i " "$RFCOMM_CHANNEL" " | cut -d' ' -f1 | cut -d':' -f1) + +if test "x"$BINDED_CHANNEL != "x"; then + MESSAGE1="A bind between the bluetooth device (using the dial-up-networking profile) and /dev/rfcomm$DEV_NUMBER already exists. You can use any connection program (kppp, pppd script etc...) using /dev/rfcomm$DEV_NUMBER like the modem device" + + kdialog --msgbox "$MESSAGE1" +else + +# Find the first unbinded /dev/rfcommX +DEV_NUMBER=0 +while test "x"$(echo $RFCOMM_SHOW_OUT | grep "rfcomm"$DEV_NUMBER":"| cut -d' ' -f1 ) != "x"; do + DEV_NUMBER=$[$DEV_NUMBER+1] + if test $DEV_NUMBER -gt 255; then break; fi; +done; + + +# do the bind +echo "doing: rfcomm bind $DEV_NUMBER $BTADDR $RFCOMM_CHANNEL" + +if test -e /def/rfcomm$DEV_NUMBER; then +EXIT_CODE=$(tdesu -f/dev/rfcomm$DEV_NUMBER -c "rfcomm bind $DEV_NUMBER $BTADDR $RFCOMM_CHANNEL") +else +EXIT_CODE=$(tdesu -c "rfcomm bind $DEV_NUMBER $BTADDR $RFCOMM_CHANNEL") +fi + +echo $EXIT_CODE + +MESSAGE1="Created a bind between the bluetooth device (using the dial-up-networking profile) and /dev/rfcomm$DEV_NUMBER. You can use any connection program (kppp, pppd script etc...) using /dev/rfcomm$DEV_NUMBER like the modem device" + +kdialog --msgbox "$MESSAGE1" + +fi; + + diff --git a/src/tdebluez-common/dunhandler/dunhandler.desktop.in b/src/tdebluez-common/dunhandler/dunhandler.desktop.in new file mode 100644 index 0000000..057ba2c --- /dev/null +++ b/src/tdebluez-common/dunhandler/dunhandler.desktop.in @@ -0,0 +1,10 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=@dunhandler_dir@/kdebluetooth/dunhandler/dunhandler +Exec=dunhandler %u +Icon=network_local +Type=Application +Comment=An OBEX Object Push client for the KdeBluetooth Framework +Comment[et]=OBEX objekti saatmise klient KdeBluetooth raamistikule +MimeType=bluetooth/dun-profile; +Hidden=true diff --git a/src/tdebluez-common/faxhandler/Makefile.am b/src/tdebluez-common/faxhandler/Makefile.am new file mode 100644 index 0000000..61db809 --- /dev/null +++ b/src/tdebluez-common/faxhandler/Makefile.am @@ -0,0 +1,7 @@ + +dunhandlerdir = $(kde_datadir)/kdebluetooth/faxhandler +dunhandler_SCRIPTS=faxhandler kbtfax + +# The desktop file +servicemenudir = $(kde_appsdir)/Utilities +servicemenu_DATA = faxhandler.desktop diff --git a/src/tdebluez-common/faxhandler/Makefile.in b/src/tdebluez-common/faxhandler/Makefile.in new file mode 100644 index 0000000..f4e7952 --- /dev/null +++ b/src/tdebluez-common/faxhandler/Makefile.in @@ -0,0 +1,804 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# KDE tags expanded automatically by am_edit - $Revision$ +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = kdebluetooth/kdebluetooth-common/faxhandler +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(SHELL) $(top_srcdir)/admin/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = faxhandler.desktop +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(dunhandlerdir)" \ + "$(DESTDIR)$(servicemenudir)" +SCRIPTS = $(dunhandler_SCRIPTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(servicemenu_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(srcdir)/faxhandler.desktop.in \ + $(top_srcdir)/admin/mkinstalldirs +#>- DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +#>+ 1 +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) $(KDE_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +ARTSCCONFIG = @ARTSCCONFIG@ +AUTOCONF = @AUTOCONF@ +AUTODIRS = @AUTODIRS@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BLUETOOTH_CFLAGS = @BLUETOOTH_CFLAGS@ +BLUETOOTH_LIBS = @BLUETOOTH_LIBS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CONF_FILES = @CONF_FILES@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DBUSTQT_CFLAGS = @DBUSTQT_CFLAGS@ +DBUSTQT_LIBS = @DBUSTQT_LIBS@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ +DCOPIDL = @DCOPIDL@ +DCOPIDL2CPP = @DCOPIDL2CPP@ +DCOPIDLNG = @DCOPIDLNG@ +DCOP_DEPENDENCIES = @DCOP_DEPENDENCIES@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DOXYGEN_PROJECT_NAME = @DOXYGEN_PROJECT_NAME@ +DOXYGEN_PROJECT_NUMBER = @DOXYGEN_PROJECT_NUMBER@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ENABLE_PERMISSIVE_FLAG = @ENABLE_PERMISSIVE_FLAG@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FRAMEWORK_COREAUDIO = @FRAMEWORK_COREAUDIO@ +GMSGFMT = @GMSGFMT@ +GREP = @GREP@ +HAVE_GCC_VISIBILITY = @HAVE_GCC_VISIBILITY@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KBTOBEXSRV_BINDIR = @KBTOBEXSRV_BINDIR@ +KCFG_DEPENDENCIES = @KCFG_DEPENDENCIES@ +KCONFIG_COMPILER = @KCONFIG_COMPILER@ +KDEBLUETOOTH_VERSION = @KDEBLUETOOTH_VERSION@ +KDECONFIG = @KDECONFIG@ +KDE_CHECK_PLUGIN = @KDE_CHECK_PLUGIN@ +KDE_EXTRA_RPATH = @KDE_EXTRA_RPATH@ +KDE_HAS_DOXYGEN = @KDE_HAS_DOXYGEN@ +KDE_HAVE_DOT = @KDE_HAVE_DOT@ +KDE_INCLUDES = @KDE_INCLUDES@ +KDE_LDFLAGS = @KDE_LDFLAGS@ +KDE_MT_LDFLAGS = @KDE_MT_LDFLAGS@ +KDE_MT_LIBS = @KDE_MT_LIBS@ +KDE_NO_UNDEFINED = @KDE_NO_UNDEFINED@ +KDE_PLUGIN = @KDE_PLUGIN@ +KDE_RPATH = @KDE_RPATH@ +KDE_USE_CLOSURE_FALSE = @KDE_USE_CLOSURE_FALSE@ +KDE_USE_CLOSURE_TRUE = @KDE_USE_CLOSURE_TRUE@ +KDE_USE_FINAL_FALSE = @KDE_USE_FINAL_FALSE@ +KDE_USE_FINAL_TRUE = @KDE_USE_FINAL_TRUE@ +KDE_USE_FPIE = @KDE_USE_FPIE@ +KDE_USE_NMCHECK_FALSE = @KDE_USE_NMCHECK_FALSE@ +KDE_USE_NMCHECK_TRUE = @KDE_USE_NMCHECK_TRUE@ +KDE_USE_PIE = @KDE_USE_PIE@ +KDE_XSL_STYLESHEET = @KDE_XSL_STYLESHEET@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LDFLAGS_AS_NEEDED = @LDFLAGS_AS_NEEDED@ +LDFLAGS_NEW_DTAGS = @LDFLAGS_NEW_DTAGS@ +LIBCOMPAT = @LIBCOMPAT@ +LIBCRYPT = @LIBCRYPT@ +LIBDL = @LIBDL@ +LIBJPEG = @LIBJPEG@ +LIBOBJS = @LIBOBJS@ +LIBPNG = @LIBPNG@ +LIBPTHREAD = @LIBPTHREAD@ +LIBRESOLV = @LIBRESOLV@ +LIBS = @LIBS@ +LIBSM = @LIBSM@ +LIBSOCKET = @LIBSOCKET@ +LIBTOOL = @LIBTOOL@ +LIBTQT_LDFLAGS = @LIBTQT_LDFLAGS@ +LIBUCB = @LIBUCB@ +LIBUTIL = @LIBUTIL@ +LIBXML_CFLAGS = @LIBXML_CFLAGS@ +LIBXML_LIBS = @LIBXML_LIBS@ +LIBZ = @LIBZ@ +LIB_ARTS = @LIB_ARTS@ +LIB_BLUETOOTH = @LIB_BLUETOOTH@ +LIB_KAB = @LIB_KAB@ +LIB_KDED = @LIB_KDED@ +LIB_KFM = @LIB_KFM@ +LIB_KJS = @LIB_KJS@ +LIB_LOCKDEV = @LIB_LOCKDEV@ +LIB_POLL = @LIB_POLL@ +LIB_QPE = @LIB_QPE@ +LIB_QT = @LIB_QT@ +LIB_QUI = @LIB_QUI@ +LIB_SDP = @LIB_SDP@ +LIB_SMB = @LIB_SMB@ +LIB_TDEABC = @LIB_TDEABC@ +LIB_TDECORE = @LIB_TDECORE@ +LIB_TDEDNSSD = @LIB_TDEDNSSD@ +LIB_TDEFILE = @LIB_TDEFILE@ +LIB_TDEHTML = @LIB_TDEHTML@ +LIB_TDEIMPROXY = @LIB_TDEIMPROXY@ +LIB_TDEIO = @LIB_TDEIO@ +LIB_TDENEWSTUFF = @LIB_TDENEWSTUFF@ +LIB_TDEPARTS = @LIB_TDEPARTS@ +LIB_TDEPIM = @LIB_TDEPIM@ +LIB_TDEPRINT = @LIB_TDEPRINT@ +LIB_TDESPELL = @LIB_TDESPELL@ +LIB_TDESYCOCA = @LIB_TDESYCOCA@ +LIB_TDEUI = @LIB_TDEUI@ +LIB_TDEUNITTEST = @LIB_TDEUNITTEST@ +LIB_TDEUTILS = @LIB_TDEUTILS@ +LIB_X11 = @LIB_X11@ +LIB_XEXT = @LIB_XEXT@ +LIB_XRENDER = @LIB_XRENDER@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MAKETDEWIDGETS = @MAKETDEWIDGETS@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MCOPIDL = @MCOPIDL@ +MEINPROC = @MEINPROC@ +MKDIR_P = @MKDIR_P@ +MOC = @MOC@ +MSGFMT = @MSGFMT@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NOOPT_CFLAGS = @NOOPT_CFLAGS@ +NOOPT_CXXFLAGS = @NOOPT_CXXFLAGS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENOBEX_CFLAGS = @OPENOBEX_CFLAGS@ +OPENOBEX_LIBS = @OPENOBEX_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +QTDOCDIR = @QTDOCDIR@ +QTE_NORTTI = @QTE_NORTTI@ +QT_INCLUDES = @QT_INCLUDES@ +QT_LDFLAGS = @QT_LDFLAGS@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TOPSUBDIRS = @TOPSUBDIRS@ +TQTDCOPIDL = @TQTDCOPIDL@ +TQTDCOPIDL2CPP = @TQTDCOPIDL2CPP@ +TQTDCOPIDLNG = @TQTDCOPIDLNG@ +TQTMCOPIDL = @TQTMCOPIDL@ +TQTMOC = @TQTMOC@ +UIC = @UIC@ +UIC_TR = @UIC_TR@ +USER_INCLUDES = @USER_INCLUDES@ +USER_LDFLAGS = @USER_LDFLAGS@ +USE_EXCEPTIONS = @USE_EXCEPTIONS@ +USE_RTTI = @USE_RTTI@ +USE_THREADS = @USE_THREADS@ +VERSION = @VERSION@ +WOVERLOADED_VIRTUAL = @WOVERLOADED_VIRTUAL@ +XGETTEXT = @XGETTEXT@ +XMKMF = @XMKMF@ +XMLLINT = @XMLLINT@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_INCLUDES = @X_INCLUDES@ +X_LDFLAGS = @X_LDFLAGS@ +X_PRE_LIBS = @X_PRE_LIBS@ +X_RPATH = @X_RPATH@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +all_includes = @all_includes@ +all_libraries = @all_libraries@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dunhandler_dir = @dunhandler_dir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +faxhandler_dir = @faxhandler_dir@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +kde_appsdir = @kde_appsdir@ +kde_bindir = @kde_bindir@ +kde_confdir = @kde_confdir@ +kde_datadir = @kde_datadir@ +kde_htmldir = @kde_htmldir@ +kde_icondir = @kde_icondir@ +kde_includes = @kde_includes@ +kde_kcfgdir = @kde_kcfgdir@ +kde_libraries = @kde_libraries@ +kde_libs_htmldir = @kde_libs_htmldir@ +kde_libs_prefix = @kde_libs_prefix@ +kde_locale = @kde_locale@ +kde_mimedir = @kde_mimedir@ +kde_moduledir = @kde_moduledir@ +kde_qtver = @kde_qtver@ +kde_servicesdir = @kde_servicesdir@ +kde_servicetypesdir = @kde_servicetypesdir@ +kde_sounddir = @kde_sounddir@ +kde_styledir = @kde_styledir@ +kde_templatesdir = @kde_templatesdir@ +kde_wallpaperdir = @kde_wallpaperdir@ +kde_widgetdir = @kde_widgetdir@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +qt_includes = @qt_includes@ +qt_libraries = @qt_libraries@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +tdeinitdir = @tdeinitdir@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +x_includes = @x_includes@ +x_libraries = @x_libraries@ +xdg_appsdir = @xdg_appsdir@ +xdg_directorydir = @xdg_directorydir@ +xdg_menudir = @xdg_menudir@ +dunhandlerdir = $(kde_datadir)/kdebluetooth/faxhandler +dunhandler_SCRIPTS = faxhandler kbtfax + +# The desktop file +servicemenudir = $(kde_appsdir)/Utilities +servicemenu_DATA = faxhandler.desktop +#>- all: all-am +#>+ 1 +all: docs-am all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) +#>- @for dep in $?; do \ +#>- case '$(am__configure_deps)' in \ +#>- *$$dep*) \ +#>- ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ +#>- && { if test -f $@; then exit 0; else break; fi; }; \ +#>- exit 1;; \ +#>- esac; \ +#>- done; \ +#>- echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign kdebluetooth/kdebluetooth-common/faxhandler/Makefile'; \ +#>- $(am__cd) $(top_srcdir) && \ +#>- $(AUTOMAKE) --foreign kdebluetooth/kdebluetooth-common/faxhandler/Makefile +#>+ 12 + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign kdebluetooth/kdebluetooth-common/faxhandler/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign kdebluetooth/kdebluetooth-common/faxhandler/Makefile + cd $(top_srcdir) && perl admin/am_edit kdebluetooth/kdebluetooth-common/faxhandler/Makefile.in +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +faxhandler.desktop: $(top_builddir)/config.status $(srcdir)/faxhandler.desktop.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +install-dunhandlerSCRIPTS: $(dunhandler_SCRIPTS) + @$(NORMAL_INSTALL) + @list='$(dunhandler_SCRIPTS)'; test -n "$(dunhandlerdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(dunhandlerdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(dunhandlerdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(dunhandlerdir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(dunhandlerdir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-dunhandlerSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(dunhandler_SCRIPTS)'; test -n "$(dunhandlerdir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + dir='$(DESTDIR)$(dunhandlerdir)'; $(am__uninstall_files_from_dir) + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-servicemenuDATA: $(servicemenu_DATA) + @$(NORMAL_INSTALL) + @list='$(servicemenu_DATA)'; test -n "$(servicemenudir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(servicemenudir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(servicemenudir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(servicemenudir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(servicemenudir)" || exit $$?; \ + done + +uninstall-servicemenuDATA: + @$(NORMAL_UNINSTALL) + @list='$(servicemenu_DATA)'; test -n "$(servicemenudir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(servicemenudir)'; $(am__uninstall_files_from_dir) +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(SCRIPTS) $(DATA) +installdirs: + for dir in "$(DESTDIR)$(dunhandlerdir)" "$(DESTDIR)$(servicemenudir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +#>- clean: clean-am +#>+ 1 +clean: kde-rpo-clean clean-am + +#>- clean-am: clean-generic clean-libtool mostlyclean-am +#>+ 1 +clean-am: clean-bcheck clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-dunhandlerSCRIPTS install-servicemenuDATA + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-dunhandlerSCRIPTS uninstall-servicemenuDATA + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am \ + install-dunhandlerSCRIPTS install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am \ + install-servicemenuDATA install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ + uninstall-am uninstall-dunhandlerSCRIPTS \ + uninstall-servicemenuDATA + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: + +#>+ 2 +KDE_DIST=faxhandler kbtfax configure.in.in Makefile.am faxhandler.desktop.in Makefile.in + +#>+ 2 +docs-am: + +#>+ 15 +force-reedit: + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign kdebluetooth/kdebluetooth-common/faxhandler/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign kdebluetooth/kdebluetooth-common/faxhandler/Makefile + cd $(top_srcdir) && perl admin/am_edit kdebluetooth/kdebluetooth-common/faxhandler/Makefile.in + + +#>+ 21 +clean-bcheck: + rm -f *.bchecktest.cc *.bchecktest.cc.class a.out + +bcheck: bcheck-am + +bcheck-am: + @for i in ; do \ + if test $(srcdir)/$$i -nt $$i.bchecktest.cc; then \ + echo "int main() {return 0;}" > $$i.bchecktest.cc ; \ + echo "#include \"$$i\"" >> $$i.bchecktest.cc ; \ + echo "$$i"; \ + if ! $(CXX) $(DEFS) -I. -I$(srcdir) -I$(top_builddir) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(KDE_CXXFLAGS) --dump-class-hierarchy -c $$i.bchecktest.cc; then \ + rm -f $$i.bchecktest.cc; exit 1; \ + fi ; \ + echo "" >> $$i.bchecktest.cc.class; \ + perl $(top_srcdir)/admin/bcheck.pl $$i.bchecktest.cc.class || { rm -f $$i.bchecktest.cc; exit 1; }; \ + rm -f a.out; \ + fi ; \ + done + + +#>+ 3 +final: + $(MAKE) all-am + +#>+ 3 +final-install: + $(MAKE) install-am + +#>+ 3 +no-final: + $(MAKE) all-am + +#>+ 3 +no-final-install: + $(MAKE) install-am + +#>+ 3 +kde-rpo-clean: + -rm -f *.rpo + +#>+ 3 +nmcheck-am: nmcheck +nmcheck: diff --git a/src/tdebluez-common/faxhandler/configure.in.in b/src/tdebluez-common/faxhandler/configure.in.in new file mode 100644 index 0000000..f6b5479 --- /dev/null +++ b/src/tdebluez-common/faxhandler/configure.in.in @@ -0,0 +1,4 @@ +dnl AC_OUTPUT(kdebluetooth/kdebluetooth-common/faxhandler/faxhandler.desktop) +KDE_EXPAND_MAKEVAR(faxhandler_dir, kde_datadir) +AC_SUBST(faxhandler_dir) + diff --git a/src/tdebluez-common/faxhandler/faxhandler b/src/tdebluez-common/faxhandler/faxhandler new file mode 100755 index 0000000..6b757b5 --- /dev/null +++ b/src/tdebluez-common/faxhandler/faxhandler @@ -0,0 +1,10 @@ +#!/bin/sh + +# copyright 2004 by Fred Schaettgen + + +MESSAGE="To send faxes you can set up tdeprintfax to use the kbtfax script, which comes with kdebluetooth. Do you want to read more about that?" + +if $(kdialog --yesno "$MESSAGE" --title "KDE Bluetooth Framework") ; then + konqueror help:/kdebluetooth/otherprofiles.html#fax +fi diff --git a/src/tdebluez-common/faxhandler/faxhandler.desktop.in b/src/tdebluez-common/faxhandler/faxhandler.desktop.in new file mode 100644 index 0000000..7b8707d --- /dev/null +++ b/src/tdebluez-common/faxhandler/faxhandler.desktop.in @@ -0,0 +1,9 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=@faxhandler_dir@/kdebluetooth/faxhandler/faxhandler +Exec=faxhandler %u +Icon=network_local +Type=Application +Comment=A handler for the fax profile +MimeType=bluetooth/fax-profile; +Hidden=true diff --git a/src/tdebluez-common/faxhandler/kbtfax b/src/tdebluez-common/faxhandler/kbtfax new file mode 100755 index 0000000..577c999 --- /dev/null +++ b/src/tdebluez-common/faxhandler/kbtfax @@ -0,0 +1,105 @@ +#!/bin/bash + +# A wrapper for efax to be used together with a bluetooth mobile phone +# and tdeprintfax to send faxes via bluetooth from any kde application. +# +# This script does basically the same as the "efax" command, which is +# already configured in tdeprintfax. +# The difference is that kbtfax will ask you which bluetooth device +# you want to connect to and then sets up a virtual serial device +# with the rfcomm utility. Then the fax command is started and +# finally the rfcomm binding is released. + +# Use the following fax command when printing (without the 'FAXCMDLINE=') +FAXCMDLINE="kbtfax NAME=%user PAGE=%page FROM=%from send %res %number %files" +# This is the same as for eFax, but without the DEV parameter + +# If your device does not support the fax profile +# and works of the serial port profile instead, +# you will have to change this script to use +# SPP instead of FAX for the search UUID. +# Default: SEARCH_UUID="FAX" +SEARCH_UUID="FAX" + +# This will list all possibly relevant services, including fax, +# dialup and serial port profile. It will also list many +# unrelated services like obex push etc., but it should be +# easy to see what's the right choice. +#SEARCH_UUID="0x0003" + +# If you don't want to use the default rfcomm +# device (/dev/rfcomm1) you can specify another one here. +# Default: RFCOMM_DEVICE=1 +RFCOMM_DEVICE=1 + +# ----- End of options ----- + +BIND_ERROR=10 +FAX_ERROR=11 +RELEASE_ERROR=12 +RFCOMM_PERM_ERROR=13 +DEV_ERROR=14 + +if [ "x$1" == "x" ] ; then + + kdialog --inputbox "Use this command for \"Other fax system\" in tdeprintfax:" "$FAXCMDLINE" + + +elif [ "x$KBTFAX_ROOTMODE" != "xYES" ] ; then + +# The script was not called with the special +# argument RFCOMM, so the script was called +# by the user/kprintfax + +# We search for a service now + SEARCH_RESULT=( $(kbtsearch -u $SEARCH_UUID --title "Select fax service") ) + SEARCH_STATUS=$? + echo "Status: $SEARCH_STATUS" + + if [ $SEARCH_STATUS -eq 0 ] ; then + +# The user has selected a service + export ADDRESS=${SEARCH_RESULT[0]} + export CHANNEL=${SEARCH_RESULT[1]} + export KBTFAX_ROOTMODE="YES" + echo "Address=$ADDRESS Channel=$CHANNEL" + tdesu -t -- $0 "$@" + RET=$? + if [ $RET == $BIND_ERROR ] ; then + kdialog --error "Error binding rfcomm device." + elif [ $RET == $FAX_ERROR ] ; then + kdialog --error "Failed to send the fax." + elif [ $RET == $RELEASE_ERROR ] ; then + kdialog --error "Could not release rfcomm device." + elif [ $ERT == 0 ] ; then + kdialog --msgbox "Fax sent." + elif [ $RET == $DEV_ERROR ] ; then + kdialog --error "The rfcomm device /dev/rfcomm$RFCOMM_DEVICE did \ +not exist or had wrong permissions. Please check if the rfcomm utility is wworking correctly." + else + kdialog --error "Unknown error in kbtfax: Root mode returned with code $RET" + fi + exit $RET + else + +# The search dialog was aborted + kdialog --msgbox "Sending fax was aborted." + fi +else + +# Recursive call. We should be running as root +# now, so we can set up rfcomm and call fax + echo "Binding /dev/rfcomm$RFCOMM_DEVICE to $ADDRESS, channel $CHANNEL" + rfcomm bind $RFCOMM_DEVICE $ADDRESS $CHANNEL || exit $BIND_ERROR +# Rfcomm (or is it udev?) seems to need some time +# to set up the device + sleep 5 + [ -w /dev/rfcomm$RFCOMM_DEVICE -a -r /dev/rfcomm$RFCOMM_DEVICE ] || exit $DEV_ERROR + fax "DEV=rfcomm$RFCOMM_DEVICE" "$@" + FAXRET=$? + echo -n "Releasing rfcomm device... " + rfcomm release $ADDRESS || exit $RELEASE_ERROR + [ $FAXRET -eq 0 ] || exit $FAX_ERROR + echo "done." + kdialog --msgbox "kbtfax script root mode done" +fi diff --git a/src/tdebluez-common/icons/CMakeLists.txt b/src/tdebluez-common/icons/CMakeLists.txt new file mode 100644 index 0000000..1978ba9 --- /dev/null +++ b/src/tdebluez-common/icons/CMakeLists.txt @@ -0,0 +1,14 @@ +################################################# +# +# (C) 2018 Emanoil Kotsev +# deloptes (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +##### install icons in /opt/trinity/share/icons ############### +tde_install_icons( DESTINATION ${DATA_INSTALL_DIR}/tdebluez/icons ) + diff --git a/src/tdebluez-common/icons/bluetooth.svg b/src/tdebluez-common/icons/bluetooth.svg new file mode 100644 index 0000000..de8078d --- /dev/null +++ b/src/tdebluez-common/icons/bluetooth.svg @@ -0,0 +1,86 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/src/tdebluez-common/icons/hi128-app-bluetooth.png b/src/tdebluez-common/icons/hi128-app-bluetooth.png new file mode 100644 index 0000000000000000000000000000000000000000..c8479fbf60b5eebad3c537039bbf990d9b22b9ef GIT binary patch literal 5190 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 YzZG^fL=7{pp#T5?07*qoM6N<$g3shU)&Kwi literal 0 HcmV?d00001 diff --git a/src/tdebluez-common/icons/hi16-app-bluetooth.png b/src/tdebluez-common/icons/hi16-app-bluetooth.png new file mode 100644 index 0000000000000000000000000000000000000000..e00780b68e215e99f4878c75c011512856d8aa5a GIT binary patch literal 684 zcmV;d0#p5oP)+NB3WSu zX!q`BaVbpInMW)xg#m&QRr_XXAv}KS3-CJqi3`6%96Fma0}SJDOa&`uV8m@+U@jSd z_&iVbxhMP#BoFW&H6v1q_x}k SDQhYK0000abH`n3t$q9i*3$2M-33k{N0T4;H;=C>x~P+<*VQs85G6`EPE} z@Amuh`F{BEz#}L&Exi`2Dvt}vPLN3{d996J8ZU;5!oc2qf0vZ2L7;k1O(j#G!dXy= z0X4JS!Ppf|&l~kiMPoq82;)1T3ZVbyVeD2DA3uiy+$JF^GQWog^&NwLQ4N+3VB6Yx z0&ZVuqT`r57o4+u?m0^e1k_thMm8yywX7>J%;mOv+Pt;di%p_V-`S8aFj$Xxs_>t0 z&CIX)DE`j<|(#tEv5@}qO3WvG|SW?r4 z_<8}rlc5>@Ml_xUd~A0r9PAn(vXm-@=D$I19CkAoPt_r%#8s)_-B(?-TvXt4Dh&6X zWNdnlpYt*NkKS{my@9Kz8~#t=EVps@=_Gd^kE3cCzRpH@7@VfPrIs?A8H7RV`Mfuf z$i<175EC;Y0745YO}n?^YueIpkHk~>uD#^TY$O{9dYJ4?=sW8^Q@N+tp_%0a0j=eZjfRC-5?~QTPhzCI xzmQ)i$c_C#-)13~YgZ&G&ES~g&6#8|?jJU>v!Dv1bAkW>002ovPDHLkV1nfJAjALw literal 0 HcmV?d00001 diff --git a/src/tdebluez-common/icons/hi22-app-bluetooth.png b/src/tdebluez-common/icons/hi22-app-bluetooth.png new file mode 100644 index 0000000000000000000000000000000000000000..e08a2979f7a8c4cf8bd12fcc391504aaf8c2dc84 GIT binary patch literal 948 zcmV;l155mgP)EzNGCrUu!R z#7n9~@rJ|;X^bSaf%?$mB~8(^kq0%~7&TOUXkS`{R;x&f7ZP>ByVVC9)J9#SEo#IT zWfx>;zCP$8GdsK0^EBsw&iUp4eKR>DNElMM@^>s0$mJlsAXA^~;*mt2M)F-GSQSh( zEv&>e|5i^_b2JL1Kc?lqGmNSoP7Qm#zi*_-pJ5!BW_>033>qwWU5B zip>BH_eWW>zM1CWO?G}!NX4SO!NaHnBQg_$v1tlGfj67YYd)YQP)ugJYG~;n(AeE{ z8BN#OP&Sq9%rpRWDU`(E7jg7#h~nyl5@H%)EVsi-|pZ{NV+tghK|`Z_)h zC3PH&YoTrm${V=Yc^@Gl%j4$Dx^~JowHlySkrWItY1|0Lk4YcQr{s>p?NpLXyaKp< z!FwDI6m!2jjHXM@|Ix+FX*sm*UP4aR8?O&eQEcq0E@b;!KL<|yNnu4Jx-MzyxW$~x zU+@ffacWNq1>Wq}15eL;lhRoeS-&$Fcb{jk{>|*lpGi|4G}RU{dSt34*ltWk6rk!lvI6;RN#5=* zKpF^sI`6IrQk(@Ik;M!Q-25QSIO!+zQlKDviKnkC`y(D!4jF}QHHzj842-&-E{-7< zr{7MoJ=N?W(B{5D@W0}ugqOWGOYWl(Z`udVn z2OexXzfZs0cr(NPA5|WcSXQlSdTdd!H}2rJTmzou>C;jhpI4r~`CnqO&gm5zrzUM# zws(<6!=%Sf$+6crzOT-?J?+ee?|)rw*aM1UGHQ0;{qVQ$v5D`E(z{AaWIn`R9}KI#MH(WR9=Ml&Wn6YJYtCX;c64Wc4}RZP^bk^i0frh2z&>SM!;aBDgG* z>-+DyPo0vju9%#4^jH=-ZR4LQg|XMW3-qo%3@-q(Lqpjg-Z~C6`h0L}pvXguzS#x$ z?){npG_^9skjGi3Em1w@_l(EO-K;p8d|j9w4sGqXtb4Ze!2Tj#x#RWczkh$soT!*9 Ta*A0#3lz|vu6{1-oD!M<`^&a3 literal 0 HcmV?d00001 diff --git a/src/tdebluez-common/icons/hi22-app-media-playback-start.png b/src/tdebluez-common/icons/hi22-app-media-playback-start.png new file mode 100644 index 0000000000000000000000000000000000000000..8ea1a5a93f5de49e9d94f1471aa36442a6d18779 GIT binary patch literal 1378 zcmV-o1)chdP)iJIqvsi+~&s2>>2kq z_SoA#>>N4n`%puYOXL#CCHdhRlH4Mt+$q0Aq9nJJD5UVqd)D(cGtIVf_W5e{^?uBH z)_T_Stk=ls_2&%*kK#894+}h;YZ=C3sX~5KMKCN zxw(U}vGGp>S!t7nAW3+5xVXBy`cp$g!xxQ4lbD^I9h01#Ec5a43AVSl=iAxY1v@x6 zgj-u%i|PE!eu)1&rj;+<|YdW2(Wpra#S5qSXh|Q*VmUbJUslZtE(#$$Vxd$j z71FoR#Kgp~q@<*01Pw`wDkA)=T1&emdwn| zl$4g1CU|>$i>P(Bva-TXPfztAWo2bj0*|Pws?rC}_+DIGEXmEy{ll<&4Amc;PfSc` z1_uXok({`e$Ye6Hv$Hdso}Ok`S66I*e_s#M+S)2_Z*Nz0baW{2#<{tjp?-?y~1$ZKk99F4Sw5mZ)Is(X5RatK{bl?x*xB7|zSS_p1QejLEs+8U#3S$%!| zcOV2Biy&%a#>U2?0odEyt88m)^EcAEGO)3+@lQhhNDqlb8Uj_2=0)Jt#>R$`syG1p z5hGvx0J5~SlrTR(A3rxY7Y8sZD4KpFHPTw&pAscQ=cMb9*x1-mQiG6;C84=Q@_0P1 za#9r|`dE2+IirqQQ&SULSXj`5Y;0_NpdvmjE-og_&d$b-kB>*`0DR$4mU+3rNU(e3Y(nx8k06{0KtE*w0OtBTQxUgGbe>f8KK-Z9-nVA^}pqqnX z^6TsCzi)4E^YOmCyvzUyIsskl>+77^-eBcAIy&+N0s*&EAWb+E=7GLVOiZ|>0C<}G z?(VMW;o+g={{FrgXLJI(&;`--jg zfA&JaWkFTOL$Zb{LSh6=1a*L4s0GPTF=)}Lh`>Ewz0zn0{i;wXqUeHA>3o1W!2WP3 z^bMT@1f75`a1L5tto-SN%L4mc=VUA{>=xJ`4h0B00bKyXK!4!;XCCZxtXynGEH3O8 k*dGpsiJ)`66kmA$1*DaNgSJS@6#xJL07*qoM6N<$g09ey1poj5 literal 0 HcmV?d00001 diff --git a/src/tdebluez-common/icons/hi22-app-media-playback-stop.png b/src/tdebluez-common/icons/hi22-app-media-playback-stop.png new file mode 100644 index 0000000000000000000000000000000000000000..3156247aa3e4a20a29afeab66f30a6f47077e341 GIT binary patch literal 339 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H0wnYHF4+L2SkfJR9T^xl_H+M9WCik>lDyqr z82T3U_KE^|oCO|{#S9GGf*{OTpAtV2D9B#o>Fdh=h=-L!MqyixqB&6Lil>WXh{pM= zmlpaRPLN=|U^X+efsN&$a9k6+gMvUU%aV~mgcxa!e?|; zB6$8O+$!PBmMr^zYqI~J|GZ7Iei`RC3cEjA)$Z5I)X00MFh56Q^696`s&=iLYx?lS zk=^>QYocCx&-&0fk)M0@F7NehXD!SQ+__VHRXy_dQ=#)qf>tiHJ{tey*!JzS`cm?~ zZhJVX?|<|4679GvC5sGl>vyf2D&Bg06@QnRjOUpz^`?ba|J;2(sW)y{#-W9MC+ch$ h@0b(tc|w_n{S|Go!xF#B%7Ff3@O1TaS?83{1OSL2hOGbq literal 0 HcmV?d00001 diff --git a/src/tdebluez-common/icons/hi22-app-media-seek-backward.png b/src/tdebluez-common/icons/hi22-app-media-seek-backward.png new file mode 100644 index 0000000000000000000000000000000000000000..8b4781acfb986181c9f6e8f224e0f9f8af5b7d1f GIT binary patch literal 681 zcmV;a0#^NrP)*TG4wQBwSkZ-0T#XIg z=)y(u3k!-nw=Vqv7m6Z&EZiu#*M&d8KjdFHJ?C;0jL9UovufbM5Z?E^*W8%QE2oJLEMl^b(!J6ed$Xp9fV{$@mRo2?(6kBnFs^-E|)ZMR$Y_pgkvo zr^AH1-42$^CG>hdMx|0=Mp2z30MTQy802y}Xt&!i7z`})WHPxn{t?vKUl9^R9?6_a zrJ&Vn!FW7|)9J*_bo{vSH*K_l&bi6RJ@O?Uk3%}0W-Acg{I9yd;o;t~Z{t#t P00000NkvXXu0mjfzR)U+ literal 0 HcmV?d00001 diff --git a/src/tdebluez-common/icons/hi22-app-media-seek-forward.png b/src/tdebluez-common/icons/hi22-app-media-seek-forward.png new file mode 100644 index 0000000000000000000000000000000000000000..054b4cb8b66f9d5ed170dfb1bce849f959e08383 GIT binary patch literal 471 zcmV;|0Vw{7P)It#NA~7Klc+AXvTQA>voeD7_p~uW5n|js9cWzQ4Cg(A;lC<=sPrXg@ z97V^>O486s?|Pc#DrLvaO0uev#!5B($t~)RnU$oj2aR>F>q+i-tjQuV0VK;x^{7h? zbfr5zt0&|$8kC42sp?j#1{!FnNAPp2wn$LxzkXVa-b(J}jJpj{!3vlMyEe0=i= z(Pb}7p~vh2qQ(YW(+9{gdqi2F$`U1$d}a=OM4ou&C*a!5Qt&4Oz5tMTKfZH1&e#9| N002ovPDHLkV1joW$V&hK literal 0 HcmV?d00001 diff --git a/src/tdebluez-common/icons/hi22-app-media-skip-backward.png b/src/tdebluez-common/icons/hi22-app-media-skip-backward.png new file mode 100644 index 0000000000000000000000000000000000000000..ad460b471f14783434f21c9e57cac4445f9dcc23 GIT binary patch literal 785 zcmV+s1Md8ZP))*J0VC?NFZIXkyxbi zg}aGZ2xhyKc|ZsWMiaG2iX;kKh>t>S^?z84m49J&X2Lzm^NKp`+bt{s+mb|##5%Fh=fg&$p`^r0`uzMX$6+e=`~A@Eb|sM*iIw=|5|l}@LP6;mR-zP^f>CBz1? z+wI0gA|Xe^qL-H!tkr6Gc6KH!l}ee5xi2*__a(>m^|dx~_KeDka{=4`CO(g}vJf2Gj P00000NkvXXu0mjfpPpFH literal 0 HcmV?d00001 diff --git a/src/tdebluez-common/icons/hi22-app-media-skip-forward.png b/src/tdebluez-common/icons/hi22-app-media-skip-forward.png new file mode 100644 index 0000000000000000000000000000000000000000..82c5b4941e99721cd24c85069eaf6c1ae140819c GIT binary patch literal 494 zcmV}gB!ozi?6tb3(aCFp(Vy_pL84nf;qBe8S)DcM);r87-#jy}yE`H#o9qQ0qh#W; zOzF60x`Gwc#rlk<3)aH@xa`9RN4D_=B5CWpPWUK7;7ie5#IF1iA3fzg!w=X~aNx2z zNWm3HQZe4fD+bC3hKpqJj5{$;C1_PV+z{9GGV1naWO0hi1kDj&PsT2Bn4luF zIL3!!Q9OLK4q2F>eCKe)J2QqgI7!g7z8i;116Ip?f)*|*Ety?^YF0}Gc`fCw!w_Xp k&(C;%tsZ4(cy9hL-?XG86b6jO^#A|>07*qoM6N<$g1l4CeE zK~y-)m6dI5Q&kwpf461p+741EjF)KywlNtd&TW!td?7fD8sZ3skVuFHjX{VrViw{D zGvLD#!m=!6ycr@20z;#N5Arf%j1EB=FM}l;j5M%OV0P`+t-Zay_x9Z5hXQWxg8C%S zhm$<#m;d>noc~dzlmIA-LV7hc94%E8K5;m*eO~V*q?AOX8nKvmOiH3bgX zkW&7$7*%z32YtJDx5yhe`lMx9@?i-fWKU18+`01ySyy+^Hy&6@IX<|ycAsz0o?|i| zPe>tzj7Fm}91hE1FepQzkTeWKs;VaU?mZ@JYxnua11srPY}&Nb<8tNPs;OD!vD+2G z;UU`Et`Q7IkW%7yJE*8Aps1(-!${K5&>YcqbI!JH8zQ5vu%#cxG)>>ah4VcQM-~GE zVGbNPO?&(GEq#4|O|Gt9sdRVueR|}`Z;`84J8`?6l$X!*n5LOwNd{Qg&8m`;IYgsT zE?(^B&YfUmeSP)j?b|<&0Myr4H^yS|EoaYm5eNh*DVc+=n^pe`P9$_@&h(;b8a+J$ z5()iy`b<+(P2=ETl!1W(rcB8tk;*b27GwQkG5nT|m8Oa1H*lO!&{JV&Zq-u!ogEnc zmznSF_xN`$C>FdgJ7rAL8@PKNTBVToN_1mh+EcSEnS`km>#w{!-oX0oH9m{9)+ z<%^rqTq=qJeZi@q_CeCZe&rmY;oM9s!VwAT5U3`oCaU4$_~CY@O?6Y~%;L`N5M{6K zK+DxoY&KAA463;>qCV_p?i6qv8bugx*CMEVUxnP59_aSP4 zuu_b4#gUi<&4ic%F$1S9&gL)Pz(^#x85b;k_DRm4S&tYTd9-=5Ba%{V3NS^Akq(v; zh#R1$dd9>EpKU$Gk`=qjaoO3hZa>o|xj5Il9uZE#^PRy*P)v)_AvyL*Yi6pGg6C%V zv3qhEQBy-gj{AnkB{ifsDZZpY`?jGdI=%{0-Vge0GW|d~yH)002ovPDHLkV1l4g BOYQ&w literal 0 HcmV?d00001 diff --git a/src/tdebluez-common/icons/hi22-app-tdebluez.png b/src/tdebluez-common/icons/hi22-app-tdebluez.png new file mode 100644 index 0000000000000000000000000000000000000000..cb9d886ccdc5b2a19e844387973fc646ccf85d47 GIT binary patch literal 875 zcmV-x1C;!UP)k)h?XA`I4!hD1wxcsL1b!Tl40c%rf8WlI*fPj=|?U@-WjLf zpWpr7hwr@SejGu6nsPZHFvvL_v~VG0yQcb=IbQCl>CYxSe>$cZW6h}&nGTp0MWAUC z0Ev96z#e}bg!*n zIF9pa@rd3t8a1ieLgr?N`3AOJA1Ed1dd(ZG6Ug2-*Bu__a;hmlfsN{~7$8)y`O3X= z)-H(RTs@qC>T+@LLJ=TG`UI8& z13<>c8CWKVQ`^`^SZE+Z!09~}e%YN=)%{@Wib>?=S5Z>cWY7b9_$wg!L_P(j^+ZLO zNy~Z;K(?)f$}iuE7#7Ovj66!KnmpO@`@0Y@Dsd`cb!n0Z1=S1HU{m{g^Bae7kSPg)b(BV+2u4|>Xv5g^S zqt{6KfQ6trFjIpUnes);#$#AR@6Eu+kM*#ithXU%S6Z;*}!Aa%b%U-EHymG=U*5E7DK0 zA3-9L5r6|hDTo{GcEdG_lRLfneUs&w*Y*EfzX2HB{8Nn{OtJs~002ovPDHLkV1mLC Bg>V1> literal 0 HcmV?d00001 diff --git a/src/tdebluez-common/icons/hi32-app-bluetooth.png b/src/tdebluez-common/icons/hi32-app-bluetooth.png new file mode 100644 index 0000000000000000000000000000000000000000..6ee36fd0644d19cc4453f79262d8ef045c7c05e0 GIT binary patch literal 1351 zcmV-N1-SZ&P)1K~z|Ut(SRFlw};pKhN%R?5=AB_JzB=Sm2$7}WNUkkPuJ9%{coUeTQWqGI`Hz~%$95;YYp_}uA*D&hv((ADRY_Xq(OUVvT zo!i&~$k{xeA$>Y(Fkn-JaWAo}TKoa804|rZ7V!530AREFb8urEqXu?S7YLrSxPoT+ z1kh^G=WT)U-pm`r5@;gxqj5|a+Cxk7rcVIUR6hnV>M<`kbB*kxEBN~v*|~BwGsmh< zd89QqUA0)$0i@`u#~i?3?_&CjTy`J*6{A6rzH~SXC-r+&CR~a`HK%$FNEzDrvtvjr zQj>>b3Dm43T#Dqa_4#CEmmvhKn-@jOtQXx05c)>|(Xi}aZ7 zLQy0OHy2QF<{AJW%o#}Xw8&;g!cRZI!;lizW|VUGft@}*LMYxbnL)ifsA^3{1MBBU zGc2+*0LAC4*p++9Q+n?J0QslNiJF_q<-h(RB*@H>)CnZT_jc0+w+`f+jd9FRY}hO_ zvdegF$w6-2uJsC_J+aN=O4S{Py|bU4D@HSFU>CM5j^)qFI{?ADDCUx&Q0J?{@0U*3QwF2|>01+6G%RwT+=QSxqh(*<~F2shrB2_dO;u2bkEi zb}Z5T!mv9UPRQIJuKt%$N|$Z`DY?aP z_FXNkW*_B~=m%yqPyfO`Vg33<_u0X$d^ud>!Sv#r|DN zZ9t1BRz$a_*?#b%C-Z7hrqmWM&hzA<>*%mXr%siUjX=|VOo)b!N+U{z6iL-33om%f zt^e#Au-#&@+Gik%2iYG8Y1Zll@i&4KD9Zktl6Mb-(8g;P{{jOKq_C3u;rjpp002ov JPDHLkV1n05alrrp literal 0 HcmV?d00001 diff --git a/src/tdebluez-common/icons/hi32-app-tdebluelock.png b/src/tdebluez-common/icons/hi32-app-tdebluelock.png new file mode 100644 index 0000000000000000000000000000000000000000..066a496f4a3584b1b9b458fc7bfbe2142e1dbb82 GIT binary patch literal 2019 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 Hu0mjfC{(uG literal 0 HcmV?d00001 diff --git a/src/tdebluez-common/icons/hi64-app-bluetooth.png b/src/tdebluez-common/icons/hi64-app-bluetooth.png new file mode 100644 index 0000000000000000000000000000000000000000..47aa24ffc3890617b3db72fbbe0af007435f22d7 GIT binary patch literal 2646 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 Eg7Z8es{jB1 literal 0 HcmV?d00001 diff --git a/src/tdebluez-common/icons/hi64-app-tdebluez.png b/src/tdebluez-common/icons/hi64-app-tdebluez.png new file mode 100644 index 0000000000000000000000000000000000000000..03f6dcbd3f429a5d06b127d5687a267561697d2b GIT binary patch literal 2638 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 + + + + + + + + + image/svg+xml + + + + + + + + + T + + diff --git a/src/tdebluez-common/mimetypes/CMakeLists.txt b/src/tdebluez-common/mimetypes/CMakeLists.txt new file mode 100644 index 0000000..9e912b8 --- /dev/null +++ b/src/tdebluez-common/mimetypes/CMakeLists.txt @@ -0,0 +1,37 @@ +################################################# +# +# (C) 2018 Emanoil Kotsev +# deloptes (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +#shellmimedir = $(kde_mimedir)/bluetooth +#/opt/trinity/share/mimelnk +install( FILES + av-device-class.desktop + computer-device-class.desktop + imaging-device-class.desktop + health-device-class.desktop + keyboard-device-class.desktop + lan-device-class.desktop + misc-device-class.desktop + mouse-device-class.desktop + peripheral-device-class.desktop + phone-device-class.desktop + toy-device-class.desktop + wearable-device-class.desktop + unknown-device-class.desktop + dun-profile.desktop + fax-profile.desktop + handsfree-profile.desktop + headset-profile.desktop + obex-ftp-profile.desktop + obex-objectpush-profile.desktop + serial-port-profile.desktop + synchronization-profile.desktop + unknown-profile.desktop + DESTINATION ${MIME_INSTALL_DIR}/bluetooth ) diff --git a/src/tdebluez-common/mimetypes/av-device-class.desktop b/src/tdebluez-common/mimetypes/av-device-class.desktop new file mode 100644 index 0000000..befe686 --- /dev/null +++ b/src/tdebluez-common/mimetypes/av-device-class.desktop @@ -0,0 +1,32 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=bluetooth/av-device-class +Icon=audio-x-generic +Comment=Audio/Video Bluetooth Device +Comment[ar]=جهاز صوتي/مرئي Bluetooth +Comment[bg]=Аудио/видео Bluetooth устройство +Comment[bs]=Audio/video Bluetooth uređaj +Comment[ca]=Dispositiu Bluetooth àudio/vídeo +Comment[da]=Audio/Video Bluetooth-enhed +Comment[de]=Audio/Video Bluetooth-Gerät +Comment[el]=Συσκευή ήχου/βίντεο Bluetooth +Comment[es]=Dispositivo Bluetooth audio/vídeo +Comment[et]=Audio/video Bluetoothi seade +Comment[fr]=Périphérique Audio/Vidéo Bluetooth +Comment[gl]=Dispositivo Bluetooth Áudio/Vídeo +Comment[it]=Dispositivo Bluetooth Audio/Video +Comment[ja]=オーディオ/ビデオ Bluetooth デバイス +Comment[lt]=Audio/video Bluetooth įrenginys +Comment[nl]=Bluetooth Audio/Video-apparaat +Comment[pa]=ਆਡੀਓ/ਵੀਡਿਓ ਬਲਿਊਟੁੱਥ ਜੰਤਰ +Comment[pl]=Urządzenie Bluetooth Audio/Wideo +Comment[pt]=Dispositivo Bluetooth Áudio/Vídeo +Comment[pt_BR]=Dispositivo Bluetooth Áudio/Vídeo +Comment[sr]=Аудио-видео bluetooth уређај +Comment[sr@Latn]=Audio-video bluetooth uređaj +Comment[sv]=Blåtandsenhet med ljud eller video +Comment[ta]=அடியோ/விடியோ புலுடுத் கருவி +Comment[tg]=Дастгоҳи аудио/видео Bluetoot +Comment[tr]=Bluetooth Ses/Görüntü Aygıtı +Comment[xx]=xxAudio/Video Bluetooth Devicexx diff --git a/src/tdebluez-common/mimetypes/computer-device-class.desktop b/src/tdebluez-common/mimetypes/computer-device-class.desktop new file mode 100644 index 0000000..c20ff0f --- /dev/null +++ b/src/tdebluez-common/mimetypes/computer-device-class.desktop @@ -0,0 +1,32 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=bluetooth/computer-device-class +Icon=konsole +X-TDE-IsAlso=inode/directory +Comment=Computer/PDA Bluetooth Device +Comment[bg]=Компютърно/PDA Bluetooth устройство +Comment[bs]=Računar/PDA Bluetooth uređaj +Comment[ca]=Dispositiu Bluetooth ordinador/PDA +Comment[da]=Computer/PDA Bluetooth-enhed +Comment[de]=Computer/PDA Bluetooth-Gerät +Comment[el]=Συσκευή υπολογιστή/PDA Bluetooth +Comment[es]=Dispositivo Bluetooth computadora/PDA +Comment[et]=Arvuti/PDA Bluetoothi seade +Comment[fr]=Périphérique ordinateur ou PDA Bluetooth +Comment[gl]=Dispositivo Bluetooth Computador/PDA +Comment[it]=Dispositivo Bluetooth Computer/PDA +Comment[ja]=コンピュータ/PDA Bluetooth デバイス +Comment[lt]=Kompiuterio/PDA Bluetooth įrenginys +Comment[nl]=Bluetooth computer/PDA-apparaat +Comment[pa]=ਕੰਪਿਊਟਰ/PDA ਬਲਿਊਟੁੱਥ ਜੰਤਰ +Comment[pl]=Urządzenie Bluetooth komputer/PDA +Comment[pt]=Dispositivo Bluetooth Computador/PDA +Comment[pt_BR]=Dispositivo Bluetooth Computador/PDA +Comment[sr]=Рачунар/PDA bluetooth уређај +Comment[sr@Latn]=Računar/PDA bluetooth uređaj +Comment[sv]=Blåtandsenhet i dator eller handdator +Comment[ta]=கணிணி/PDA புலுடுத் கருவி +Comment[tg]=Дастгоҳи компютер/PDA Bluetooth +Comment[tr]=Bluetooth Bilgisayar/PDA Aygıtı +Comment[xx]=xxComputer/PDA Bluetooth Devicexx diff --git a/src/tdebluez-common/mimetypes/dun-profile.desktop b/src/tdebluez-common/mimetypes/dun-profile.desktop new file mode 100644 index 0000000..dd0d5de --- /dev/null +++ b/src/tdebluez-common/mimetypes/dun-profile.desktop @@ -0,0 +1,30 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=bluetooth/dun-profile +Icon=network_local +Comment=Bluetooth Dial-Up Networking Profile +Comment[bg]=Профил за връзка с Bluetooth (Dial-Up) +Comment[bs]=Bluetooth dial-up networking profil +Comment[ca]=Perfil de xarxa telefònica Bluetooth +Comment[da]=Bluetooth opkalds-netværkprofil +Comment[de]=Bluetooth Netzwerkeinwahl-Profil +Comment[el]=Προφίλ δικτύου μέσω τηλεφωνικής κλήσης Bluetooth +Comment[es]=Perfil de red telefónica Bluetooth +Comment[et]=Bluetoothi sissehelistamisteenuse profiil +Comment[fr]=Profil Réseau Bluetooth +Comment[gl]=Perfil de Rede Dial-Up Bluetooth +Comment[it]=Profilo di rete telefonica Bluetooth +Comment[ja]=Bluetooth ダイアルアップ ネットワーク プロファイル (DUN) +Comment[lt]=Bluetooth Dial-Up ryšio profilis +Comment[nl]=Bluetooth inbelnetwerkprofiel +Comment[pa]=ਬਲਿਊਟੁੱਥ ਡਾਇਲਅੱਪ ਨੈਟਵਰਕ ਪ੍ਰੋਫਾਇਲ +Comment[pl]=Profil sieci wdzwanianej Bluetooth +Comment[pt]=Perfil de Rede 'Dial-Up' Bluetooth +Comment[pt_BR]=Perfil de Rede 'Dial-Up' Bluetooth +Comment[sr]=Bluetooth профил повезивања преко телефона +Comment[sr@Latn]=Bluetooth profil povezivanja preko telefona +Comment[sv]=Blåtandsprofil för uppringt nätverk +Comment[ta]=புலுடுத் தொலைபேசி முலம் இனைகும் முறை +Comment[tr]=Bluetooth Çevirmeli Bağlantı Profili +Comment[xx]=xxBluetooth Dial-Up Networking Profilexx diff --git a/src/tdebluez-common/mimetypes/fax-profile.desktop b/src/tdebluez-common/mimetypes/fax-profile.desktop new file mode 100644 index 0000000..1c4ff8c --- /dev/null +++ b/src/tdebluez-common/mimetypes/fax-profile.desktop @@ -0,0 +1,31 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=bluetooth/fax-profile +Icon=printer2 +Comment=Bluetooth Fax Profile +Comment[ar]=مواصفات فاكس Bluetooth +Comment[bg]=Профил за факс с Bluetooth +Comment[bs]=Bluetooth fax profil +Comment[ca]=Perfil de fax Bluetooth +Comment[da]=Bluetooth fax-profil +Comment[de]=Bluetooth Fax-Profil +Comment[el]=Προφίλ Bluetooth Fax +Comment[es]=Perfil de fax Bluetooth +Comment[et]=Bluetoothi faksiprofiil +Comment[fr]=Profil de Fax Bluetooth +Comment[gl]=Perfil de Fax Bluetooth +Comment[it]=Profilo Fax Bluetooth +Comment[ja]=Bluetooth ファクス プロファイル (FAX) +Comment[lt]=Bluetooth fakso profilis +Comment[nl]=Bluetooth faxprofiel +Comment[pa]=ਬਲਿਊਟੁੱਥ ਫੈਕਸ ਪ੍ਰੋਫਾਇਲ +Comment[pl]=Profil Bluetooth faksowania +Comment[pt]=Perfil de Fax Bluetooth +Comment[pt_BR]=Perfil de Fax Bluetooth +Comment[sr]=Bluetooth профил за факс +Comment[sr@Latn]=Bluetooth profil za faks +Comment[sv]=Blåtandsprofil för telefax +Comment[ta]=புலுடுத் தொலை நகல் பக்கநோக்கு +Comment[tr]=Bluetooth Faks Profili +Comment[xx]=xxBluetooth Fax Profilexx diff --git a/src/tdebluez-common/mimetypes/handsfree-profile.desktop b/src/tdebluez-common/mimetypes/handsfree-profile.desktop new file mode 100644 index 0000000..90ecf70 --- /dev/null +++ b/src/tdebluez-common/mimetypes/handsfree-profile.desktop @@ -0,0 +1,29 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=bluetooth/handsfree-profile +Icon=kaddressbook +Comment=Bluetooth Hands-Free Profile +Comment[bg]=Профил Hands-Free за Bluetooth +Comment[bs]=Bluetooth hands-free profil +Comment[ca]=Perfil de mans lliures Bluetooth +Comment[da]=Bluetooth Hands-Free-profil +Comment[de]=Bluetooth Hands-Free-Profil +Comment[el]=Προφίλ Bluetooth Hands-Free +Comment[es]=Perfil manos libres Bluetooth +Comment[et]=Bluetoothi handsfree profiil +Comment[fr]=Profil Hands-Free Bluetooth +Comment[gl]=Perfil de Maos-Libres Bluetooth +Comment[it]=Profilo viva voce Bluetooth +Comment[ja]=Bluetooth ハンズフリー プロファイル (HFP) +Comment[lt]=Bluetooth laisvų rankų profilis +Comment[nl]=Bluetooth Hands-Free-profiel +Comment[pl]=Profil Bluetooth zestawu słuchawkowego +Comment[pt]=Perfil Mãos-Livres Bluetooth +Comment[pt_BR]=Perfil Mãos-Livres Bluetooth +Comment[sr]=Bluetooth хендсфри профил +Comment[sr@Latn]=Bluetooth hendsfri profil +Comment[sv]=Blåtandsprofil för handsfree +Comment[ta]=புலுடுத் கைஇல்லா பக்கநோக்கு +Comment[tr]=Bluetooth Eller Serbest Profili +Comment[xx]=xxBluetooth Hands-Free Profilexx diff --git a/src/tdebluez-common/mimetypes/headset-profile.desktop b/src/tdebluez-common/mimetypes/headset-profile.desktop new file mode 100644 index 0000000..e5c38cf --- /dev/null +++ b/src/tdebluez-common/mimetypes/headset-profile.desktop @@ -0,0 +1,30 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=bluetooth/headset-profile +Icon=kaddressbook +Comment=Bluetooth Headset Profile +Comment[bg]=Профил Headset за Bluetooth +Comment[bs]=Bluetooth headset profil +Comment[ca]=Perfil d'auriculars Bluetooth +Comment[da]=Bluetooth Headset-profil +Comment[de]=Bluetooth Headset-Profil +Comment[el]=Προφίλ Bluetooth ακουστικών +Comment[es]=Perfil de auriculares Bluetooth +Comment[et]=Bluetoothi peakomplekti profiil +Comment[fr]=Profil Headset Bluetooth +Comment[gl]=Perfil de Auricular Bluetooth +Comment[it]=Profilo cuffie Bluetooth +Comment[ja]=Bluetooth ヘッドセット プロファイル (HSP) +Comment[lt]=Bluetooth ausinių profilis +Comment[nl]=Bluetooth Headset-profiel +Comment[pa]=ਬਲਿਊਟੁੱਥ ਹੈਡਸੈਟ ਪ੍ਰੋਫਾਇਲ +Comment[pl]=Profil Bluetooth zestawu słuchawek z mikrofonem +Comment[pt]=Perfil de Auricular Bluetooth +Comment[pt_BR]=Perfil de Auricular Bluetooth +Comment[sr]=Bluetooth профил за слушалице +Comment[sr@Latn]=Bluetooth profil za slušalice +Comment[sv]=Blåtandsprofil för headset +Comment[ta]=புலுடுத் தலைஅமைப்பு பக்கநோக்கு +Comment[tr]=Bluetooth Kulaklıklı Mikrofon Profili +Comment[xx]=xxBluetooth Headset Profilexx diff --git a/src/tdebluez-common/mimetypes/health-device-class.desktop b/src/tdebluez-common/mimetypes/health-device-class.desktop new file mode 100644 index 0000000..fbc682d --- /dev/null +++ b/src/tdebluez-common/mimetypes/health-device-class.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=bluetooth/health-device-class +Icon=health +X-TDE-IsAlso=inode/directory +Comment=Health Bluetooth Device +Comment[bg]=Медицинско Bluetooth устройство +Comment[xx]=xxHealth Bluetooth Devicexx diff --git a/src/tdebluez-common/mimetypes/imaging-device-class.desktop b/src/tdebluez-common/mimetypes/imaging-device-class.desktop new file mode 100644 index 0000000..24b5f48 --- /dev/null +++ b/src/tdebluez-common/mimetypes/imaging-device-class.desktop @@ -0,0 +1,30 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=bluetooth/imaging-device-class +Icon=camera_unmount +Comment=Imaging Bluetooth Device +Comment[bg]=Bluetooth устройство за снимка +Comment[bs]=Imaging Bluetooth uređaj +Comment[ca]=Dispositiu Bluetooth d'imatges +Comment[da]=Imaging Bluetooth-enhed +Comment[de]=Imaging Bluetooth-Gerät +Comment[el]=Συσκευή εικόνας Bluetooth +Comment[es]=Dispositivo Bluetooth de imágenes +Comment[et]=Bluetoothi pildiseade +Comment[fr]=Périphérique d'imagerie Bluetooth +Comment[gl]=Dispositivo Bluetooth con Imaxe +Comment[it]=Dispositivo fotografico Bluetooth +Comment[ja]=イメージング Bluetooth デバイス +Comment[lt]=Bluetooth paveikslėlių įrenginys +Comment[nl]=Bluetooth imaging-apparaat +Comment[pa]=ਚਿੱਤਰ ਬਲਿਊਟੁੱਥ ਜੰਤਰ +Comment[pl]=Urządzenie Bluetooth obrazowania +Comment[pt]=Dispositivo Bluetooth com Imagem +Comment[pt_BR]=Dispositivo Bluetooth com Imagem +Comment[sr]=Bluetooth уређај за прављење слика +Comment[sr@Latn]=Bluetooth uređaj za pravljenje slika +Comment[sv]=Blåtandsenhet med kamera +Comment[ta]=புலுடுத் நினைக்கும் கருவி +Comment[tr]=Bluetooth Görüntü Aygıtı +Comment[xx]=xxImaging Bluetooth Devicexx diff --git a/src/tdebluez-common/mimetypes/keyboard-device-class.desktop b/src/tdebluez-common/mimetypes/keyboard-device-class.desktop new file mode 100644 index 0000000..1445b7b --- /dev/null +++ b/src/tdebluez-common/mimetypes/keyboard-device-class.desktop @@ -0,0 +1,29 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=bluetooth/keyboard-device-class +Icon=keyboard +Comment=Peripheral Bluetooth Device +Comment[bg]=Периферно Bluetooth устройство +Comment[ca]=Dispositiu Bluetooth perifèric +Comment[da]=Perifer Bluetooth-enhed +Comment[de]=Peripheriegerät für Bluetooth +Comment[el]=Περιφερειακή συσκευή Bluetooth +Comment[es]=Dispositivo Bluetooth periférico +Comment[et]=Väline Bluetoothi seade +Comment[fr]=Périphérique Bluetooth +Comment[gl]=Dispositivo Bluetooth Periférico +Comment[it]=Dispositivo Bluetooth periferico +Comment[ja]=周辺機器 Bluetooth デバイス +Comment[lt]=Periferinis Bluetooth įrenginys +Comment[nl]=Bluetooth randapparaat +Comment[pa]=ਪੈਰਾਫੀਰਲ ਬਲਿਊਟੁੱਥ ਜੰਤਰ +Comment[pl]=Urządzenie peryferyjne Bluetooth +Comment[pt]=Dispositivo Bluetooth Periférico +Comment[pt_BR]=Dispositivo Bluetooth Periférico +Comment[sr]=Периферијски bluetooth уређај +Comment[sr@Latn]=Periferijski bluetooth uređaj +Comment[sv]=Extern Blåtandsenhet +Comment[ta]=புற புலுடுத் கருவி +Comment[tr]=Çevresel Bluetooth Aygıtı +Comment[xx]=xxPeripheral Bluetooth Devicexx diff --git a/src/tdebluez-common/mimetypes/lan-device-class.desktop b/src/tdebluez-common/mimetypes/lan-device-class.desktop new file mode 100644 index 0000000..5035fd7 --- /dev/null +++ b/src/tdebluez-common/mimetypes/lan-device-class.desktop @@ -0,0 +1,30 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=bluetooth/lan-device-class +Icon=nfs_unmount +Comment=LAN/Networking Bluetooth Device +Comment[bg]=Мрежово/LAN Bluetooth устройство +Comment[ca]=Dispositiu Bluetooth de xarxa +Comment[da]=LAN/Netværk Bluetooth-enhed +Comment[de]=LAN/Netwerk Bluetooth-Gerät +Comment[el]=Δικτυακή συσκευή Bluetooth +Comment[es]=Dispositivo Bluetooth de redes +Comment[et]=Kohtvõrgu/interneti Bluetoothi seade +Comment[fr]=Périphérique LAN/Réseau Bluetooth +Comment[gl]=Dispositivo de Rede Bluetooth +Comment[it]=Dispositivo Bluetooth di LAN/Rete +Comment[ja]=LAN/ネットワーク Bluetooth デバイス +Comment[lt]=Bluetooth LAN/tinklo įrenginys +Comment[nl]=Bluetooth LAN-/netwerk-apparaat +Comment[pa]=LAN/ਨੈਟਵਰਕ ਬਲਿਊਟੁੱਥ ਜੰਤਰ +Comment[pl]=Urządzenie Bluetooth LAN/sieciowe +Comment[pt]=Dispositivo de Rede Bluetooth +Comment[pt_BR]=Dispositivo de Rede Bluetooth +Comment[sr]=LAN/мрежни bluetooth уређај +Comment[sr@Latn]=LAN/mrežni bluetooth uređaj +Comment[sv]=Blåtandsenhet för lokalt nätverk +Comment[ta]=LAN/புலுடுத் வலை இனைப்பு கருவி +Comment[tg]=Дастгоҳи шабака/LAN Bluetooth +Comment[tr]=LAN/Ağ Bluetooth Aygıtı +Comment[xx]=xxLAN/Networking Bluetooth Devicexx diff --git a/src/tdebluez-common/mimetypes/misc-device-class.desktop b/src/tdebluez-common/mimetypes/misc-device-class.desktop new file mode 100644 index 0000000..67a510b --- /dev/null +++ b/src/tdebluez-common/mimetypes/misc-device-class.desktop @@ -0,0 +1,30 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=bluetooth/misc-device-class +Icon=tdebluez +X-TDE-IsAlso=inode/directory +Comment=Miscellaneous Bluetooth Device +Comment[bg]=Разни Bluetooth устройства +Comment[ca]=Dispositiu Bluetooth divers +Comment[da]=Diverse Bluetooth-enheder +Comment[de]=Vielseitiges Bluetooth-Gerät +Comment[el]=Συσκευή Bluetooth άλλης κατηγορίας +Comment[es]=Dispositivo Bluetooth diverso +Comment[et]=Mitmesugused Bluetoothi seadmed +Comment[fr]=Périphérique Bluetooth divers +Comment[gl]=Dispositivo Bluetooth +Comment[it]=Dispositivo Bluetooth vario +Comment[ja]=その他の Bluetooth デバイス +Comment[lt]=Kitas Bluetooth įrenginys +Comment[nl]=Ander Bluetooth-apparaat +Comment[pa]=ਫੁਟਕਲ ਬਲਿਊਟੁੱਥ ਜੰਤਰ +Comment[pl]=Inne urządzenie Bluetooth +Comment[pt]=Dispositivo Bluetooth Variado +Comment[pt_BR]=Dispositivo Bluetooth Variado +Comment[sr]=Неодређени bluetooth уређај +Comment[sr@Latn]=Neodređeni bluetooth uređaj +Comment[sv]=Annan Blåtandsenhet +Comment[ta]=இதர புலுடுத் கருவி +Comment[tr]=Çeşitli Bluetooth Aygıtları +Comment[xx]=xxMiscellaneous Bluetooth Devicexx diff --git a/src/tdebluez-common/mimetypes/mouse-device-class.desktop b/src/tdebluez-common/mimetypes/mouse-device-class.desktop new file mode 100644 index 0000000..00a5880 --- /dev/null +++ b/src/tdebluez-common/mimetypes/mouse-device-class.desktop @@ -0,0 +1,29 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=bluetooth/mouse-device-class +Icon=mouse +Comment=Peripheral Bluetooth Device +Comment[bg]=Периферно Bluetooth устройство +Comment[ca]=Dispositiu Bluetooth perifèric +Comment[da]=Perifer Bluetooth-enhed +Comment[de]=Peripheriegerät für Bluetooth +Comment[el]=Περιφερειακή συσκευή Bluetooth +Comment[es]=Dispositivo Bluetooth periférico +Comment[et]=Väline Bluetoothi seade +Comment[fr]=Périphérique Bluetooth +Comment[gl]=Dispositivo Bluetooth Periférico +Comment[it]=Dispositivo Bluetooth periferico +Comment[ja]=周辺機器 Bluetooth デバイス +Comment[lt]=Periferinis Bluetooth įrenginys +Comment[nl]=Bluetooth randapparaat +Comment[pa]=ਪੈਰਾਫੀਰਲ ਬਲਿਊਟੁੱਥ ਜੰਤਰ +Comment[pl]=Urządzenie peryferyjne Bluetooth +Comment[pt]=Dispositivo Bluetooth Periférico +Comment[pt_BR]=Dispositivo Bluetooth Periférico +Comment[sr]=Периферијски bluetooth уређај +Comment[sr@Latn]=Periferijski bluetooth uređaj +Comment[sv]=Extern Blåtandsenhet +Comment[ta]=புற புலுடுத் கருவி +Comment[tr]=Çevresel Bluetooth Aygıtı +Comment[xx]=xxPeripheral Bluetooth Devicexx diff --git a/src/tdebluez-common/mimetypes/obex-ftp-profile.desktop b/src/tdebluez-common/mimetypes/obex-ftp-profile.desktop new file mode 100644 index 0000000..a16bcbd --- /dev/null +++ b/src/tdebluez-common/mimetypes/obex-ftp-profile.desktop @@ -0,0 +1,29 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=bluetooth/obex-ftp-profile +Icon=nfs_unmount +Comment=OBEX File Transfer Profile +Comment[bg]=Профил за пренос на OBEX файл +Comment[ca]=Perfil de transferència de fitxers OBEX +Comment[da]=OBEX filoverførselsprofil +Comment[de]=OBEX Dateitransferprotokoll +Comment[el]=Προφίλ μεταφοράς αρχείων OBEX +Comment[es]=Perfil de transferencia de archivos OBEX +Comment[et]=OBEX failiedastuse profiil +Comment[fr]=Profil de transfert de fichiers via OBEX +Comment[gl]=Perfil de Transferéncia de Ficheiros OBEX +Comment[it]=Profilo di trasferimento file OBEX +Comment[ja]=OBEX ファイル転送プロファイル (FTP) +Comment[lt]=OBEX bylų persiuntimo profilis +Comment[nl]=OBEX bestandsoverdrachtprofiel +Comment[pa]=OBEX ਫਾਇਲ ਸੰਚਾਰ ਪ੍ਰੋਫਾਇਲ +Comment[pl]=Profil transferu plików OBEX +Comment[pt]=Perfil de Transferência de Ficheiros OBEX +Comment[pt_BR]=Perfil de Transferência de Ficheiros OBEX +Comment[sr]=Профил за OBEX пренос фајлова +Comment[sr@Latn]=Profil za OBEX prenos fajlova +Comment[sv]=OBEX-filöverföringsprofil +Comment[ta]=OBEX கோப்பு மாற்று பக்கநோக்கு +Comment[tr]=OBEX Dosya Transfer Profili +Comment[xx]=xxOBEX File Transfer Profilexx diff --git a/src/tdebluez-common/mimetypes/obex-objectpush-profile.desktop b/src/tdebluez-common/mimetypes/obex-objectpush-profile.desktop new file mode 100644 index 0000000..d1b0ce6 --- /dev/null +++ b/src/tdebluez-common/mimetypes/obex-objectpush-profile.desktop @@ -0,0 +1,32 @@ +[Desktop Entry] +Encoding=UTF-8 +Comment=OBEX Object Push Profile +Comment[bg]=Профил OBEX Object Push +Comment[ca]=Perfil d'enviament d'objectes OBEX +Comment[da]=OBEX objekt-skubbeprofil +Comment[de]=OBEX Object-Push-Profil +Comment[el]=Προφίλ αποστολής αντικειμένων OBEX +Comment[es]=Perfil de envío de objetos OBEX +Comment[et]=OBEX objekti saatmise profiil +Comment[fr]=Profil OBEX Object Push +Comment[gl]=Perfil de Envio de Obxectos OBEX +Comment[it]=Profilo di Oggetto Push OBEX +Comment[ja]=OBEX オブジェクトプッシュ プロファイル (OPP) +Comment[lt]=OBEX persiuntimo profilis +Comment[nl]=OBEX Object Push-profiel +Comment[pa]=OBEX Object Push ਪ੍ਰੋਫਾਇਲ +Comment[pl]=Profil OBEX Object Push +Comment[pt]=Perfil de Envio de Objectos OBEX +Comment[pt_BR]=Perfil de Envio de Objectos OBEX +Comment[sr]=Профил за OBEX гурање објеката +Comment[sr@Latn]=Profil za OBEX guranje objekata +Comment[sv]=OBEX-objektsändingsprofil +Comment[ta]=OBEX பொருள் விளக்கக்குறிப்பு +Comment[tr]=OBEX Nesne Ekleme Profili +Comment[xx]=xxOBEX Object Push Profilexx +Icon=mail_send +Type=MimeType +MimeType=bluetooth/obex-object-push-profile +Patterns= +NoDisplay=false +X-TDE-AutoEmbed=true diff --git a/src/tdebluez-common/mimetypes/peripheral-device-class.desktop b/src/tdebluez-common/mimetypes/peripheral-device-class.desktop new file mode 100644 index 0000000..55eaa3f --- /dev/null +++ b/src/tdebluez-common/mimetypes/peripheral-device-class.desktop @@ -0,0 +1,29 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=bluetooth/peripheral-device-class +Icon=joystick +Comment=Peripheral Bluetooth Device +Comment[bg]=Периферно Bluetooth устройство +Comment[ca]=Dispositiu Bluetooth perifèric +Comment[da]=Perifer Bluetooth-enhed +Comment[de]=Peripheriegerät für Bluetooth +Comment[el]=Περιφερειακή συσκευή Bluetooth +Comment[es]=Dispositivo Bluetooth periférico +Comment[et]=Väline Bluetoothi seade +Comment[fr]=Périphérique Bluetooth +Comment[gl]=Dispositivo Bluetooth Periférico +Comment[it]=Dispositivo Bluetooth periferico +Comment[ja]=周辺機器 Bluetooth デバイス +Comment[lt]=Periferinis Bluetooth įrenginys +Comment[nl]=Bluetooth randapparaat +Comment[pa]=ਪੈਰਾਫੀਰਲ ਬਲਿਊਟੁੱਥ ਜੰਤਰ +Comment[pl]=Urządzenie peryferyjne Bluetooth +Comment[pt]=Dispositivo Bluetooth Periférico +Comment[pt_BR]=Dispositivo Bluetooth Periférico +Comment[sr]=Периферијски bluetooth уређај +Comment[sr@Latn]=Periferijski bluetooth uređaj +Comment[sv]=Extern Blåtandsenhet +Comment[ta]=புற புலுடுத் கருவி +Comment[tr]=Çevresel Bluetooth Aygıtı +Comment[xx]=xxPeripheral Bluetooth Devicexx diff --git a/src/tdebluez-common/mimetypes/phone-device-class.desktop b/src/tdebluez-common/mimetypes/phone-device-class.desktop new file mode 100644 index 0000000..b1b28f8 --- /dev/null +++ b/src/tdebluez-common/mimetypes/phone-device-class.desktop @@ -0,0 +1,32 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=bluetooth/phone-device-class +Icon=kaddressbook +X-TDE-IsAlso=inode/directory +Comment=Phone Bluetooth Device +Comment[ar]=جهاز هاتف Bluetooth +Comment[bg]=Телефонно Bluetooth устройство +Comment[ca]=Dispositiu Bluetooth telefònic +Comment[da]=Telefon Bluetooth-enhed +Comment[de]=Bluetooth-Gerät für Telefonie +Comment[el]=Τηλεφωνική συσκευή Bluetooth +Comment[es]=Dispositivo bluetooth telefónico +Comment[et]=Telefoni Bluetoothi seade +Comment[fr]=Téléphone Bluetooth +Comment[gl]=Teléfone con Bluetooth +Comment[it]=Dispositivo Bluetooth telefonico +Comment[ja]=電話 Bluetooth デバイス +Comment[lt]=Bluetooth telefono įrenginys +Comment[nl]=Bluetooth telefoon +Comment[pa]=ਫੋਨ ਬਲਿਊਟੁੱਥ ਜੰਤਰ +Comment[pl]=Urządzenie Bluetooth - telefon +Comment[pt]=Dispositivo Bluetooth Telefone +Comment[pt_BR]=Dispositivo Bluetooth Telefone +Comment[sr]=Телефонски bluetooth уређај +Comment[sr@Latn]=Telefonski bluetooth uređaj +Comment[sv]=Blåtandsenhet i telefon +Comment[ta]=தொலைபெசி புலுடுத் கருவி +Comment[tg]=Дастгоҳи телефони Bluetooth +Comment[tr]=Bluetooth Telefon Aygıtı +Comment[xx]=xxPhone Bluetooth Devicexx diff --git a/src/tdebluez-common/mimetypes/serial-port-profile.desktop b/src/tdebluez-common/mimetypes/serial-port-profile.desktop new file mode 100644 index 0000000..439de4a --- /dev/null +++ b/src/tdebluez-common/mimetypes/serial-port-profile.desktop @@ -0,0 +1,29 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=bluetooth/serial-port-profile +Icon=input_devices_settings +Comment=Bluetooth Serial Port Profile +Comment[bg]=Профил за сериен порт за Bluetooth +Comment[ca]=Perfil de port sèrie Bluetooth +Comment[da]=Bluetooth seriel port profil +Comment[de]=Serielles Bluetooth-Anschlussprofil +Comment[el]=Προφίλ σειριακής θύρας Bluetooth +Comment[es]=Perfil Bluetooth de puerto serie +Comment[et]=Bluetoothi jadapordi profiil +Comment[fr]=Profil de port série Bluetooth +Comment[gl]=Perfil Bluetooth de Porto Série +Comment[it]=Profilo di porta seriale Bluetooth +Comment[ja]=Bluetooth シリアルポート プロファイル (SPP) +Comment[lt]=Bluetooth nuoseklaus prievado profilis +Comment[nl]=Bluetooth seriële-poort-profiel +Comment[pa]=ਬਲਿਊਟੁੱਥ ਸੀਰੀਅਲ ਪੋਰਟ ਪ੍ਰੋਫਾਇਲ +Comment[pl]=Profil Bluetooth portu szeregowego +Comment[pt]=Perfil Bluetooth de Portal Série +Comment[pt_BR]=Perfil Bluetooth de Portal Série +Comment[sr]=Bluetooth профил серијског порта +Comment[sr@Latn]=Bluetooth profil serijskog porta +Comment[sv]=Blåtandsprofil för serieport +Comment[ta]=புலுடுத் தொடர் துரை பக்கநோக்கு +Comment[tr]=Bluetooth Seri Port Profili +Comment[xx]=xxBluetooth Serial Port Profilexx diff --git a/src/tdebluez-common/mimetypes/synchronization-profile.desktop b/src/tdebluez-common/mimetypes/synchronization-profile.desktop new file mode 100644 index 0000000..50efd3c --- /dev/null +++ b/src/tdebluez-common/mimetypes/synchronization-profile.desktop @@ -0,0 +1,31 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=bluetooth/synchronization-profile +Icon=pda_blue +Comment=Bluetooth Synchronization Profile +Comment[ar]=مواصفات مقارنة التزامن Bluetooth +Comment[bg]=Профил за синхронизиране на Bluetooth +Comment[ca]=Perfil de sincronització Bluetooth +Comment[da]=Bluetooth synkroniseringsprofil +Comment[de]=Bluetooth Synchronisationsprofil +Comment[el]=Προφίλ συγχρονισμού Bluetooth +Comment[en_GB]=Bluetooth Synchronisation Profile +Comment[es]=Perfil de sincronización Bluetooth +Comment[et]=Bluetoothi sünkroprofiil +Comment[fr]=Profil de Synchronisation Bluetooth +Comment[gl]=Perfil de Sincronizazón Bluetooth +Comment[it]=Profilo di sincronizzazione Bluetooth +Comment[ja]=Bluetooth 同期プロファイル (SYNC) +Comment[lt]=Bluetooth sinchronizacijos profilis +Comment[nl]=Bluetooth synchronisatieprofiel +Comment[pa]=ਬਲਿਊਟੁੱਥ ਸਮਕਾਲੀ ਪ੍ਰੋਫਾਇਲ +Comment[pl]=Profil Bluetooth synchronizacji +Comment[pt]=Perfil de Sincronização Bluetooth +Comment[pt_BR]=Perfil de Sincronização Bluetooth +Comment[sr]=Bluetooth профил синхронизације +Comment[sr@Latn]=Bluetooth profil sinhronizacije +Comment[sv]=Blåtandsprofil för synkronisering +Comment[ta]=புலுடுத் ஒத்திசை பக்கநோக்கு +Comment[tr]=Bluetooth Eşzamanlama Profili +Comment[xx]=xxBluetooth Synchronization Profilexx diff --git a/src/tdebluez-common/mimetypes/toy-device-class.desktop b/src/tdebluez-common/mimetypes/toy-device-class.desktop new file mode 100644 index 0000000..b2b40d3 --- /dev/null +++ b/src/tdebluez-common/mimetypes/toy-device-class.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=bluetooth/toy-device-class +Icon=package_toys +X-TDE-IsAlso=inode/directory +Comment=Toy Bluetooth Device +Comment[bg]=Играчка Bluetooth устройство +Comment[xx]=xxToy Bluetooth Devicexx diff --git a/src/tdebluez-common/mimetypes/unknown-device-class.desktop b/src/tdebluez-common/mimetypes/unknown-device-class.desktop new file mode 100644 index 0000000..5a05925 --- /dev/null +++ b/src/tdebluez-common/mimetypes/unknown-device-class.desktop @@ -0,0 +1,30 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=bluetooth/unknown-device-class +Icon=tdebluez +Comment=Unknown Bluetooth Device Class +Comment[ar]=فئة جهاز Bluetooth غير معروف +Comment[bg]=Неизвестен клас на Bluetooth устройство +Comment[ca]=Classe de dispositiu Bluetooth desconegut +Comment[da]=Ukendt Bluetooth enhedsklasse +Comment[de]=Unbekannte Bluetooth-Geräteklasse +Comment[el]=Άγνωστη κλάση συσκευής Bluetooth +Comment[es]=Clase de dispositivo Bluetooth desconocida +Comment[et]=Tundmatu Bluetoothi seadme klass +Comment[fr]=Classe du périphérique Bluetooth inconnue +Comment[gl]=Clase de dispositivo Bluetooth descoñecida +Comment[it]=Classe di dispositivo Bluetooth ignota +Comment[ja]=未知の Bluetooth デバイスクラス +Comment[lt]=Nežinoma Bluetooth įrenginio klasė +Comment[nl]=Onbekende Bluetooth apparaatklasse +Comment[pa]=ਅਣਜਾਣ ਬਲਿਊਟੁੱਥ ਜੰਤਰ ਕਲਾਸ +Comment[pl]=Nieznana klasa urządzenia Bluetooth +Comment[pt]=Classe de dispositivo Bluetooth desconhecida +Comment[pt_BR]=Classe de dispositivo Bluetooth desconhecida +Comment[sr]=Непозната класа bluetooth уређаја +Comment[sr@Latn]=Nepoznata klasa bluetooth uređaja +Comment[sv]=Okänd klass av Blåtandsenhet +Comment[ta]=தேரியாத புலுடுத் கருவி வகுப்புகள் +Comment[tr]=Bilinmeyen Bluetooth Aygıt Sınıfı +Comment[xx]=xxUnknown Bluetooth Device Classxx diff --git a/src/tdebluez-common/mimetypes/unknown-profile.desktop b/src/tdebluez-common/mimetypes/unknown-profile.desktop new file mode 100644 index 0000000..926c29e --- /dev/null +++ b/src/tdebluez-common/mimetypes/unknown-profile.desktop @@ -0,0 +1,33 @@ +[Desktop Entry] +Encoding=UTF-8 +Comment=Unknown Bluetooth Profile +Comment[ar]=مواصفات Bluetooth غير معروفة +Comment[bg]=Неизвестен профил на Bluetooth +Comment[br]=Profil Bluetooth dianav +Comment[ca]=Perfil Bluetooth desconegut +Comment[da]=Ukendt Bluetooth-profil +Comment[de]=Unkekanntes Bluetooth Profil +Comment[el]=Άγνωστο προφίλ Bluetooth +Comment[es]=Perfil Bluetooth desconocido +Comment[et]=Tundmatu Bluetoothi profiil +Comment[fr]=Profil Bluetooth inconnu +Comment[gl]=Perfil Bluetooth Descoñecido +Comment[it]=Profilo Bluetooth ignoto +Comment[ja]=未知の Bluetooth プロファイル +Comment[lt]=Nežinomas Bluetooth profilis +Comment[nl]=Onbekend Bluetooth-profiel +Comment[pa]=ਅਣਜਾਣ ਬਲਿਊਟੁੱਥ ਪ੍ਰੋਫਾਇਲ +Comment[pl]=Nieznany profil Bluetooth +Comment[pt]=Perfil Bluetooth Desconhecido +Comment[pt_BR]=Perfil Bluetooth Desconhecido +Comment[sr]=Непознати bluetooth профил +Comment[sr@Latn]=Nepoznati bluetooth profil +Comment[sv]=Okänd Blåtandsprofil +Comment[ta]=தெரியாத ப்ளூ டூத் விளக்கக்குறிப்பு +Comment[tg]=Профили Bluetooth номуайян +Comment[tr]=Bilinmeyen Bluetooth Profili +Comment[xx]=xxUnknown Bluetooth Profilexx +Icon=misc +Type=MimeType +MimeType=bluetooth/unknown-profile +Patterns= diff --git a/src/tdebluez-common/mimetypes/wearable-device-class.desktop b/src/tdebluez-common/mimetypes/wearable-device-class.desktop new file mode 100644 index 0000000..fe1f182 --- /dev/null +++ b/src/tdebluez-common/mimetypes/wearable-device-class.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=bluetooth/wearable-device-class +Icon=kaddressbook +X-TDE-IsAlso=inode/directory +Comment=Wearable Bluetooth Device +Comment[bg]=Преносимо Bluetooth устройство +Comment[xx]=xxWearable Bluetooth Devicexx diff --git a/src/tdebluez-common/org.trinitydesktop.tdebluez.conf b/src/tdebluez-common/org.trinitydesktop.tdebluez.conf new file mode 100644 index 0000000..fa48877 --- /dev/null +++ b/src/tdebluez-common/org.trinitydesktop.tdebluez.conf @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/tdebluez-common/org.trinitydesktop.tdebluez.service b/src/tdebluez-common/org.trinitydesktop.tdebluez.service new file mode 100644 index 0000000..9b0928a --- /dev/null +++ b/src/tdebluez-common/org.trinitydesktop.tdebluez.service @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=org.trinitydesktop.tdebluez +Exec=/bin/false +User=root diff --git a/src/tdebluez-common/tde-settings-network-bluetooth.directory b/src/tdebluez-common/tde-settings-network-bluetooth.directory new file mode 100644 index 0000000..54636e7 --- /dev/null +++ b/src/tdebluez-common/tde-settings-network-bluetooth.directory @@ -0,0 +1,7 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Bluetooth +Name[pa]=ਬਲਿਊਟੁੱਥ +Name[sv]=Blåtand +Name[xx]=xxBluetoothxx +Icon=tdebluez diff --git a/src/tdebluez/CMakeLists.txt b/src/tdebluez/CMakeLists.txt new file mode 100644 index 0000000..8d1efcc --- /dev/null +++ b/src/tdebluez/CMakeLists.txt @@ -0,0 +1,48 @@ +################################################# +# +# (C) 2018 Emanoil Kotsev +# deloptes (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +project (tdebluez) + +include_directories( + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/src/libtdebluez + ${CMAKE_BINARY_DIR}/src/libtdebluez + ${CMAKE_SOURCE_DIR}/src/tdebluezauth +# ${DBUS_INCLUDE_DIRS} + ${DBUS_TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + +##### tdebluez (tdeinit) ###################### +tde_add_tdeinit_executable( tdebluez AUTOMOC + SOURCES + adapterconfig.cpp adapterdialog.ui application.cpp + devicedialog.ui devicedialog_ext.ui adapterconfigdialog.cpp + devicewizard.cpp devicesetupwizarddialog.ui devicesetupwizard.cpp + mediacontrol.cpp mediactl.ui trayicon.cpp main.cpp + LINK + ${DBUS_TQT_LIBRARIES} tdecore-shared tdeui-shared tdeio-shared tdebluez-shared bluezinterfaces-static + ${XEXT_LIBRARIES} ${XTST_LIBRARIES} ${XSCRNSAVER_LIBRARIES} + DESTINATION ${BIN_INSTALL_DIR} +) + +##### other data ################################ +# +install( FILES tdebluez.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} ) +install( FILES tdebluez.autostart.desktop DESTINATION ${AUTOSTART_INSTALL_DIR} ) +install( FILES eventsrc DESTINATION ${DATA_INSTALL_DIR}/tdebluez ) diff --git a/src/tdebluez/adapterconfig.cpp b/src/tdebluez/adapterconfig.cpp new file mode 100644 index 0000000..cf9470a --- /dev/null +++ b/src/tdebluez/adapterconfig.cpp @@ -0,0 +1,386 @@ +/* + * + * Adapter config dialog for tdebluez + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of tdebluez. + * + * tdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * tdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include "adapterconfig.h" +#include + +#include + +AdapterConfig::AdapterConfig(ObjectManagerImpl *mgr, AdapterImpl *a) +{ + adapterDialog = new AdapterDialog(); + manager = mgr; + adapter = a; + + TQT_DBusError dbuserr; + name = adapter->getAlias(dbuserr); + if (dbuserr.isValid()) + tqDebug("Get alias for the adapter failed: %s", dbuserr.message().local8Bit().data()); + + adapterDialog->deviceModes->insertItem(i18n("Hidden")); + adapterDialog->deviceModes->insertItem(i18n("Always visible")); + adapterDialog->deviceModes->insertItem(i18n("Temporary visible")); + + adapterDialog->deviceClass->insertItem(i18n("Unknown")); + adapterDialog->deviceClass->insertItem(i18n("Computer")); + adapterDialog->deviceClass->setEnabled(false); + + adapterDialog->adapterName->setText(name); + + addAdapterDialogInfo(); + + // ADAPTER -> MANAGER -> DIALOG + connect(manager, SIGNAL(adapterNameChanged(const TQString&,const TQString&)), + this, TQT_SLOT(slotAdapterNameChanged(const TQString&, const TQString&))); + connect(manager, SIGNAL(adapterAliasChanged(const TQString&,const TQString&)), + this, TQT_SLOT(slotAdapterNameChanged(const TQString&, const TQString&))); + + connect(manager, SIGNAL(adapterDiscoverableTimeoutChanged(const TQString&, TQ_UINT32)), + this, TQT_SLOT(slotDiscoverableTimeoutChanged(const TQString&, TQ_UINT32))); + connect(manager, SIGNAL(adapterDiscoverableChanged(const TQString&, bool)), + this, TQT_SLOT(slotDiscoverableChanged(const TQString&, bool))); + connect(manager, SIGNAL(adapterPowerOnChanged(const TQString&, bool)), this, TQT_SLOT(slotPowerOnChanged(const TQString&, bool))); + + // DIALOG -> ADAPTER + connect(adapterDialog->adapterName, SIGNAL(textChanged(const TQString &)), + this, TQT_SLOT(slotChangeName(const TQString &))); + connect(adapterDialog->deviceModes, SIGNAL(activated(int)), + this, TQT_SLOT(slotSetMode(int))); +// connect(adapterDialog->deviceClass, SIGNAL(activated(const TQString &)), + // this, TQT_SLOT(slotSetClass(const TQString &))); + connect(adapterDialog->sliderTimeout, SIGNAL(valueChanged(int)), + this, TQT_SLOT(slotChangeTimeout(int))); + + adapterDialog->setModal(false); +} + +AdapterConfig::~AdapterConfig() +{ + if (adapterDialog) + delete adapterDialog; +} + +void AdapterConfig::addAdapterDialogInfo() +{ + TQT_DBusError dbuserr; + bool disc = adapter->getDiscoverable(dbuserr); + int timeout = adapter->getDiscoverableTimeout(dbuserr); + if (disc) + { + if (timeout > 0) + { + adapterDialog->deviceModes->setCurrentItem(2); + adapterDialog->timeout->setNum(int(timeout) / 60); + adapterDialog->timeout->setText(adapterDialog->timeout->text().append(i18n("min"))); + adapterDialog->sliderTimeout->setValue(timeout); + + adapterDialog->sliderTimeout->setEnabled(true); + adapterDialog->timeout->setEnabled(true); + adapterDialog->textLabel9->setEnabled(true); + + } + else + { + adapterDialog->deviceModes->setCurrentItem(1); + adapterDialog->timeout->setNum(int(timeout) / 60); + adapterDialog->timeout->setText(adapterDialog->timeout->text().append(i18n("min"))); + adapterDialog->sliderTimeout->setValue(timeout); + + adapterDialog->sliderTimeout->setEnabled(false); + adapterDialog->timeout->setEnabled(false); + adapterDialog->textLabel9->setEnabled(false); + } + } + else + { + adapterDialog->deviceModes->setCurrentItem(0); + adapterDialog->timeout->setText(i18n("none")); + adapterDialog->sliderTimeout->setValue(0); + + adapterDialog->sliderTimeout->setEnabled(false); + adapterDialog->timeout->setEnabled(false); + adapterDialog->textLabel9->setEnabled(false); + } + + slotSetClass(DeviceMimeConverter::classToMimeType(adapter->getClass(dbuserr))); + if (dbuserr.isValid()) + tqDebug("Get class for the adapter failed: %s", dbuserr.message().local8Bit().data()); + + TQString address = adapter->getAddress(dbuserr); + if (dbuserr.isValid()) + tqDebug("Get address for the adapter failed: %s", dbuserr.message().local8Bit().data()); + TQStringList uuids = adapter->getUUIDs(dbuserr); + if (dbuserr.isValid()) + tqDebug("Get uuids for the adapter failed: %s", dbuserr.message().local8Bit().data()); + TQVBoxLayout *infoLayout = new TQVBoxLayout(adapterDialog->groupBoxInfo->layout()); + // GridLayout *infoGrid = new TQGridLayout(adapterDialog->groupBoxInfo->layout()); + + if (!address.isNull()) + adapterDialog->macLabel->setText(i18n("MAC Address: %1").arg(address)); + + if (!uuids.isEmpty()) + { + TQString supported; + // DeviceList; + for (TQStringList::iterator it = uuids.begin(); it != uuids.end(); ++it) + { + supported += resolveUUID((*it)) + ", "; + } + adapterDialog->verLabel->setText(i18n("

Services:
%1

").arg(supported.left(supported.length() - 2))); + } + +} + +//from adapter + +void AdapterConfig::slotAdapterNameChanged(const TQString &path, const TQString &n) +{ + if (path != adapter->getPath()) + return; + name = n; + disconnect(adapterDialog->adapterName, SIGNAL(textChanged(const TQString &)), + this, TQT_SLOT(slotChangeName(const TQString &))); + + adapterDialog->adapterName->setText(name); + + connect(adapterDialog->adapterName, SIGNAL(textChanged(const TQString &)), + this, TQT_SLOT(slotChangeName(const TQString &))); +} + +void AdapterConfig::slotPowerOnChanged(const TQString &path, bool state) +{ + if (path != adapter->getPath()) + return; + + addAdapterDialogInfo(); + + adapterDialog->setEnabled(state); +} + +void AdapterConfig::slotDiscoverableChanged(const TQString &path, bool changed) +{ + if (path != adapter->getPath()) + return; + + TQT_DBusError dbuserr; + TQ_UINT32 timeout = adapter->getDiscoverableTimeout(dbuserr); + if (dbuserr.isValid()) + tqDebug("Get discoverable timeout for the adapter failed: %s", dbuserr.message().local8Bit().data()); + disconnect(adapterDialog->deviceModes, SIGNAL(activated(int)), + this, TQT_SLOT(slotSetMode(int))); + disconnect(adapterDialog->sliderTimeout, SIGNAL(valueChanged(int)), + this, TQT_SLOT(slotChangeTimeout(int))); + + if (changed) + { + if (timeout > 0) + { + adapterDialog->deviceModes->setCurrentItem(2); + adapterDialog->timeout->setNum(int(timeout) / 60); + adapterDialog->timeout->setText(adapterDialog->timeout->text().append(i18n("min"))); + adapterDialog->sliderTimeout->setValue(timeout); + adapterDialog->sliderTimeout->setEnabled(false); + adapterDialog->timeout->setEnabled(false); + adapterDialog->textLabel9->setEnabled(false); + } + else + { + adapterDialog->deviceModes->setCurrentItem(1); + adapterDialog->timeout->setNum(int(timeout) / 60); + adapterDialog->timeout->setText(adapterDialog->timeout->text().append(i18n("min"))); + adapterDialog->sliderTimeout->setValue(timeout); + adapterDialog->sliderTimeout->setEnabled(true); + adapterDialog->timeout->setEnabled(true); + adapterDialog->textLabel9->setEnabled(true); + } + } + else + { + adapterDialog->deviceModes->setCurrentItem(0); + adapterDialog->sliderTimeout->setValue(0); + adapterDialog->timeout->setText(i18n("none")); + adapterDialog->sliderTimeout->setEnabled(false); + adapterDialog->timeout->setEnabled(false); + adapterDialog->textLabel9->setEnabled(false); + } + connect(adapterDialog->deviceModes, SIGNAL(activated(int)), + this, TQT_SLOT(slotSetMode(int))); + connect(adapterDialog->sliderTimeout, SIGNAL(valueChanged(int)), + this, TQT_SLOT(slotChangeTimeout(int))); +} + +void AdapterConfig::slotDiscoverableTimeoutChanged(const TQString &path, TQ_UINT32 timeout) +{ + if (path != adapter->getPath()) + return; + + disconnect(adapterDialog->sliderTimeout, SIGNAL(valueChanged(int)), + this, TQT_SLOT(slotChangeTimeout(int))); + + if (timeout == 0) + { + adapterDialog->sliderTimeout->setValue(0); + adapterDialog->timeout->setText(i18n("none")); + adapterDialog->sliderTimeout->setEnabled(false); + adapterDialog->timeout->setEnabled(false); + adapterDialog->textLabel9->setEnabled(false); + } + else + { + adapterDialog->timeout->setNum(int(timeout) / 60); + adapterDialog->timeout->setText(adapterDialog->timeout->text().append(i18n("min"))); + adapterDialog->sliderTimeout->setValue(int(timeout)); + adapterDialog->sliderTimeout->setEnabled(true); + adapterDialog->timeout->setEnabled(true); + adapterDialog->textLabel9->setEnabled(true); + } + connect(adapterDialog->sliderTimeout, SIGNAL(valueChanged(int)), + this, TQT_SLOT(slotChangeTimeout(int))); +} + +// from dialog +void AdapterConfig::slotChangeName(const TQString &n) +{ + name = n; + TQT_DBusError dbuserr; + if (adapterDialog->adapterName->text() != name) + { + //otherwise signal textchanged() from the dialog and slot nameChanged + //from the adapter will end in a endless loop. + disconnect(manager, SIGNAL(adapterNameChanged(const TQString&,const TQString&)), + this, TQT_SLOT(slotAdapterNameChanged(const TQString&, const TQString&))); + disconnect(manager, SIGNAL(adapterAliasChanged(const TQString&,const TQString&)), + this, TQT_SLOT(slotAdapterNameChanged(const TQString&, const TQString&))); + +// adapterDialog->adapterName->setText(name); + adapter->setAlias(name, dbuserr); + if (dbuserr.isValid()) + tqDebug("Set alias for the adapter failed: %s", dbuserr.message().local8Bit().data()); + + connect(manager, SIGNAL(adapterNameChanged(const TQString&,const TQString&)), + this, TQT_SLOT(slotAdapterNameChanged(const TQString&, const TQString&))); + connect(manager, SIGNAL(adapterAliasChanged(const TQString&,const TQString&)), + this, TQT_SLOT(slotAdapterNameChanged(const TQString&, const TQString&))); + } +} + +void AdapterConfig::slotChangeTimeout(int timeout) +{ + // Disconnect signal of valueChanged! + // D-Bus signal get emmited immeditaly .. + // and would trigger a endless loop of signals! + TQT_DBusError dbuserr; + disconnect(manager, SIGNAL(adapterDiscoverableChanged(const TQString&, bool)), + this, TQT_SLOT(slotDiscoverableChanged(const TQString&, bool))); + disconnect(manager, SIGNAL(adapterDiscoverableTimeoutChanged(const TQString&, TQ_UINT32)), + this, TQT_SLOT(slotDiscoverableTimeoutChanged(const TQString&, TQ_UINT32))); + + adapterDialog->timeout->setNum(int(timeout) / 60); + adapterDialog->sliderTimeout->setValue(int(timeout)); + adapterDialog->timeout->setText(adapterDialog->timeout->text().append(i18n("min"))); + adapter->setDiscoverableTimeout(timeout, dbuserr); + if (dbuserr.isValid()) + tqDebug("Set discoverable timeout for the adapter failed: %s", dbuserr.message().local8Bit().data()); + + connect(manager, SIGNAL(adapterDiscoverableChanged(const TQString&, bool)), + this, TQT_SLOT(slotDiscoverableChanged(const TQString&, bool))); + connect(manager, SIGNAL(adapterDiscoverableTimeoutChanged(const TQString&, TQ_UINT32)), + this, TQT_SLOT(slotDiscoverableTimeoutChanged(const TQString&, TQ_UINT32))); +} + +void AdapterConfig::slotSetMode(int modenr) +{ + + TQT_DBusError dbuserr; + int timeout = adapter->getDiscoverableTimeout(dbuserr); + if (dbuserr.isValid()) + tqDebug("Get discoverable timeout for the adapter failed: %s", dbuserr.message().local8Bit().data()); + + // Disconnect signal of valueChanged! + // D-Bus signal get emmited immeditaly .. + // and would trigger a endless loop of signals! + disconnect(adapterDialog->deviceModes, SIGNAL(activated(int)), + this, TQT_SLOT(slotSetMode(int))); + disconnect(adapterDialog->sliderTimeout, SIGNAL(valueChanged(int)), + this, TQT_SLOT(slotChangeTimeout(int))); + disconnect(manager, SIGNAL(adapterDiscoverableChanged(const TQString&, bool)), + this, TQT_SLOT(slotDiscoverableChanged(const TQString&, bool))); + disconnect(manager, SIGNAL(adapterDiscoverableTimeoutChanged(const TQString&, TQ_UINT32)), + this, TQT_SLOT(slotDiscoverableTimeoutChanged(const TQString&, TQ_UINT32))); + + switch (modenr) + { + case 0: + case 1: + timeout = 0; + adapterDialog->timeout->setText(i18n("none")); + adapterDialog->sliderTimeout->setValue(timeout); + + adapterDialog->sliderTimeout->setEnabled(false); + adapterDialog->timeout->setEnabled(false); + adapterDialog->textLabel9->setEnabled(false); + break; + case 2: + if (timeout == 0) + timeout = 180; + adapterDialog->timeout->setNum(timeout / 60); + adapterDialog->timeout->setText(adapterDialog->timeout->text().append(i18n("min"))); + adapterDialog->sliderTimeout->setValue(timeout); + + adapterDialog->sliderTimeout->setEnabled(true); + adapterDialog->timeout->setEnabled(true); + adapterDialog->textLabel9->setEnabled(true); + break; + } + adapter->setDiscoverable(((modenr > 0) ? true : false), dbuserr); + adapter->setDiscoverableTimeout(timeout, dbuserr); + if (dbuserr.isValid()) + tqDebug("Get discoverable for the adapter failed: %s", dbuserr.message().local8Bit().data()); + + connect(adapterDialog->deviceModes, SIGNAL(activated(int)), + this, TQT_SLOT(slotSetMode(int))); + connect(adapterDialog->sliderTimeout, SIGNAL(valueChanged(int)), + this, TQT_SLOT(slotChangeTimeout(int))); + + connect(manager, SIGNAL(adapterDiscoverableChanged(const TQString&, bool)), + this, TQT_SLOT(slotDiscoverableChanged(const TQString&, bool))); + connect(manager, SIGNAL(adapterDiscoverableTimeoutChanged(const TQString&, TQ_UINT32)), + this, TQT_SLOT(slotDiscoverableTimeoutChanged(const TQString&, TQ_UINT32))); +} + +void AdapterConfig::slotSetClass(const TQString & deviceClass) +{ + if (deviceClass == "bluetooth/computer-device-class") + { + adapterDialog->deviceClass->setCurrentItem(1); + } + else + { + adapterDialog->deviceClass->setCurrentItem(0); + } +} + +#include "adapterconfig.moc" diff --git a/src/tdebluez/adapterconfig.h b/src/tdebluez/adapterconfig.h new file mode 100644 index 0000000..2936580 --- /dev/null +++ b/src/tdebluez/adapterconfig.h @@ -0,0 +1,83 @@ +/* + * + * Adapter config dialog for tdebluez + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of tdebluez. + * + * tdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * tdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#ifndef ADAPTERCONFIG_H_ +#define ADAPTERCONFIG_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "adapterdialog.h" +#include "application.h" + +using namespace TDEBluetooth; + +class AdapterConfig: public TQObject +{ + Q_OBJECT + +public: + AdapterConfig(ObjectManagerImpl *mgr, AdapterImpl *adapter); + ~AdapterConfig(); + + AdapterDialog *dialog() const + { + return adapterDialog; + } + ; + TQString getName() + { + return name; + } + ; + +public slots: + // from adapter + void slotAdapterNameChanged(const TQString&, const TQString&); + void slotPowerOnChanged(const TQString&, bool); + void slotDiscoverableChanged(const TQString&, bool); + void slotDiscoverableTimeoutChanged(const TQString&, TQ_UINT32 timeout); + // from dialog + void slotChangeName(const TQString& name); + void slotSetMode(int); + void slotSetClass(const TQString& mime); + void slotChangeTimeout(int timeout); + +private: + AdapterDialog *adapterDialog; + AdapterImpl *adapter; + ObjectManagerImpl *manager; + TQString name; + void addAdapterDialogInfo(); +}; + +#endif // ADAPTERCONFIG_H_ diff --git a/src/tdebluez/adapterconfigdialog.cpp b/src/tdebluez/adapterconfigdialog.cpp new file mode 100644 index 0000000..750a4a3 --- /dev/null +++ b/src/tdebluez/adapterconfigdialog.cpp @@ -0,0 +1,141 @@ +/* + * + * Adapter Manager Gui for tdebluez + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of tdebluez. + * + * tdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * tdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "adapterconfig.h" +#include "adapterconfigdialog.h" + +AdapterConfigDialog::AdapterConfigDialog(TDEBluetoothApp *_app) : + KDialogBase(NULL, "AdapterConfigDialog", true, "Adapter Configuration", (Ok)), app(_app), manager(_app->manager) +{ + nodevice = NULL; + tabWidget = new TQTabWidget(this); + + ObjectManagerImpl::AdapterList list = manager->getAdapters(); + ObjectManagerImpl::AdapterList::iterator it; + for (it = list.begin(); it != list.end(); ++it) + addAdapter((*it)); + + if (list.count() == 0) + { + nodevice = new TQLabel(i18n("No Bluetooth adapter found!"), this); + tabWidget->addTab(nodevice, i18n("no adapter")); + tabWidget->setMinimumSize(250, 300); + } + + tabWidget->show(); + setMainWidget(tabWidget); + setModal(false); + + connect(this, SIGNAL(okClicked()), this, TQT_SLOT(hide())); + connect(manager, SIGNAL(adapterAdded(const TQString&)), TQT_SLOT(addAdapter(const TQString&))); + connect(manager, SIGNAL(adapterRemoved(const TQString&)), TQT_SLOT(removeAdapter(const TQString&))); + connect(tabWidget, SIGNAL(currentChanged(TQWidget *)), this, TQT_SLOT(slotCurrentChanged(TQWidget *))); +} + +AdapterConfigDialog::~AdapterConfigDialog() +{ + close(); + if (nodevice) + delete nodevice; + delete tabWidget; +// tabWidget = 0; +} + +void AdapterConfigDialog::addAdapter(const TQString &path) +{ + AdapterConfig *aconfig = new AdapterConfig(app->manager, app->adapters[path]); + tabWidget->addTab(aconfig->dialog(), aconfig->getName()); + + TQT_DBusError dbuserr; + bool powered = app->adapters[path]->getPowered(dbuserr); + if (dbuserr.isValid()) + tqDebug("Adapter getPowered failed: %s", dbuserr.message().local8Bit().data()); + + aconfig->dialog()->setEnabled(powered); +// +// if (tabWidget->isTabEnabled(aconfig->dialog())) +// { + tabWidget->showPage(aconfig->dialog()); +// } + adapterList.insert(path, aconfig); + + connect(aconfig->dialog()->adapterName, SIGNAL(textChanged(const TQString &)), + this, TQT_SLOT(slotChangeName(const TQString &))); + connect(aconfig->dialog()->adapterName, SIGNAL(textChanged(const TQString &)), + app->adapters[path], TQT_SLOT(slotSetAlias(const TQString &))); + + if (nodevice) + { + tabWidget->removePage(nodevice); + nodevice = 0; + } +} + +void AdapterConfigDialog::removeAdapter(const TQString &path) +{ + AdapterConfig *aconfig = adapterList[path]; + if (!aconfig) + return; + + tabWidget->removePage(aconfig->dialog()); + delete adapterList[path]; + adapterList.remove(path); + + if (adapterList.count() == 0) + { + nodevice = new TQLabel(i18n("No Bluetooth device found!"), tabWidget); + tabWidget->addTab(nodevice, i18n("no device")); + if (tabWidget->isTabEnabled(nodevice)) + { + tabWidget->showPage(nodevice); + } + } +} + +void AdapterConfigDialog::slotChangeName(const TQString &name) +{ + tabWidget->changeTab(tabWidget->currentPage(), name); +} + +void AdapterConfigDialog::slotCurrentChanged(TQWidget *widget) +{ + TQMap::iterator it; + for (it = adapterList.begin(); it != adapterList.end(); ++it) + { + if (it.data()->dialog() == widget) + { + TQString path = it.key(); + TQT_DBusError dbuserr; + TQString name = app->adapters[path]->getAlias(dbuserr); + if (dbuserr.isValid()) + tqDebug("Adapter getAlias failed: %s", dbuserr.message().local8Bit().data()); +// kdDebug() << "Adapter changed: " << it.data()->dialog()->macLabel->text() << endl; + emit signalAdapterSelected(path,name); + break; + } + } +} + +#include "adapterconfigdialog.moc" diff --git a/src/tdebluez/adapterconfigdialog.h b/src/tdebluez/adapterconfigdialog.h new file mode 100644 index 0000000..7cb0157 --- /dev/null +++ b/src/tdebluez/adapterconfigdialog.h @@ -0,0 +1,70 @@ +/* + * + * Adapter Manager Gui for tdebluez + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of tdebluez. + * + * tdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * tdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#ifndef ADAPTERCONFIGDIALOG_H_ +#define ADAPTERCONFIGDIALOG_H_ + +#include +#include + +#include + +#include "application.h" +#include "adapterconfig.h" + +using namespace TDEBluetooth; + +class AdapterConfigDialog: public KDialogBase +{ + Q_OBJECT + +public: + AdapterConfigDialog(TDEBluetoothApp *app); + ~AdapterConfigDialog(); + +public slots: + void addAdapter(const TQString&); + void removeAdapter(const TQString&); + void slotChangeName(const TQString&); + void slotCurrentChanged(TQWidget *); + +signals: + void signalAdapterSelected(const TQString&, const TQString&); + +private: + TQLabel *nodevice; + + TDEBluetoothApp *app; + ObjectManagerImpl *manager; + TQMap adapterList; + + void addAdapterDialog(AdapterImpl *adapter); + void addAdapterDialogInfo(AdapterImpl &adapater, + AdapterDialog *adapterDialog); + + TQTabWidget *tabWidget; +}; + +#endif // ADAPTERCONFIGDIALOG_H_ + diff --git a/src/tdebluez/adapterdialog.ui b/src/tdebluez/adapterdialog.ui new file mode 100644 index 0000000..c4789f7 --- /dev/null +++ b/src/tdebluez/adapterdialog.ui @@ -0,0 +1,346 @@ + +AdapterDialog + + + AdapterDialog + + + + 0 + 0 + 404 + 385 + + + + AdapterDialog + + + + + groupBoxSettings + + + + 21 + 21 + 360 + 218 + + + + + 5 + 0 + 0 + 0 + + + + + 360 + 0 + + + + + + + + Device Settings + + + + unnamed + + + + layout11 + + + + unnamed + + + + layout13 + + + + unnamed + + + + layoutName + + + + unnamed + + + + textLabel10 + + + + + + + Adapter Name: + + + + + adapterName + + + + + + + + + + + layout12 + + + + unnamed + + + + textLabel1 + + + + + + + Mode: + + + + + deviceModes + + + + + + + 0 + + + + + + + layout10 + + + + unnamed + + + + textLabel9 + + + + + + + Discoverable Timeout: + + + + + timeout + + + + + + + + + + AlignVCenter|AlignRight + + + + + + + sliderTimeout + + + + 0 + 0 + 0 + + + + 60 + + + 900 + + + 60 + + + 120 + + + Horizontal + + + NoMarks + + + Timeout after the adapter gets invisible + + + + + + + layout8 + + + + unnamed + + + + textLabel1_2 + + + + + + + Class of Device: + + + + + + Unspecified + + + + + Desktop + + + + + Laptop + + + + deviceClass + + + + + + + + + + + + + + + groupBoxInfo + + + + 21 + 245 + 360 + 131 + + + + + 3 + 3 + 0 + 0 + + + + + 360 + 0 + + + + + + + + Device Information + + + + unnamed + + + + layout14 + + + + unnamed + + + + macLabel + + + + 5 + 0 + 0 + 0 + + + + + + + + + + + AlignTop + + + + + verLabel + + + + + + + + + + + + + + + + + diff --git a/src/tdebluez/application.cpp b/src/tdebluez/application.cpp new file mode 100644 index 0000000..7ad5a27 --- /dev/null +++ b/src/tdebluez/application.cpp @@ -0,0 +1,372 @@ +/* + * + * New Bluetooth App for TDE and bluez5 + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of tdebluez. + * + * tdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * tdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#include +#include +#include + +#include + +#include +#include + +#include "trayicon.h" +#include "application.h" + +#define WAIT_BEFORE_KILL 5000 // 5sec +#define CONFIGURATION_FILE "tdebluezrc" +#define TDEBLUEZAUTH_EXE "tdebluezauth" +#define OBEX_EXE "/usr/lib/bluetooth/obexd" +#define OBEX_EXE_ALT "/usr/libexec/bluetooth/obexd" +#define BLUEZ_DN "org.bluez" +#define DOWNLOAD_DIRECTORY "Downloads" + +TDEBluetoothApp::TDEBluetoothApp() : + KUniqueApplication() +{ + // set default config file + m_config = new TDEConfig(CONFIGURATION_FILE); + m_config->setGroup("General"); + bool autostart = m_config->readBoolEntry("autoStart", false); + m_waitBeforeKill = m_config->readNumEntry("waitBeforeKill", WAIT_BEFORE_KILL); + TQString authAgentExe = m_config->readEntry("authAgentExe", TDEBLUEZAUTH_EXE); + TQString obexSrvExe = m_config->readEntry("obexSrvExe", OBEX_EXE); + TQString downloadDir = m_config->readPathEntry("downloadDir", ""); + + if (!autostart) + disableSessionManagement(); + + if (m_waitBeforeKill == 0) + { + m_waitBeforeKill = WAIT_BEFORE_KILL; + m_config->writeEntry("waitBeforeKill", m_waitBeforeKill); + } + + if (obexSrvExe.isEmpty()) + { + obexSrvExe=TQString(OBEX_EXE); + } + + if (authAgentExe.isEmpty()) + { + authAgentExe = TDEBLUEZAUTH_EXE; + m_config->writePathEntry("authAgentExe", authAgentExe); + } + + if (downloadDir.isEmpty()) + { + downloadDir = DOWNLOAD_DIRECTORY; + m_config->writePathEntry("downloadDir", downloadDir); + } + + manager = new ObjectManagerImpl(BLUEZ_DN, "/", this, "ObjectManager"); + if (!manager->isConnectedToDBUS()) + { + tqDebug("ObjectManager is not connected to DBus"); + return; + } + + ObjectManagerImpl::AdapterList al = manager->getAdapters(); + ObjectManagerImpl::AdapterList::Iterator ait = al.begin(); + for (ait; ait != al.end(); ++ait) + { + AdapterImpl *a = new AdapterImpl(BLUEZ_DN, (*ait)); + a->setConnection((*(manager->getConnection()))); + adapters.insert((*ait), a); + } + + ObjectManagerImpl::DeviceList dl = manager->getDevices(); + ObjectManagerImpl::DeviceList::Iterator dit = dl.begin(); + for (dit; dit != dl.end(); ++dit) + { + DeviceImpl *d = new DeviceImpl(BLUEZ_DN, (*dit)); + d->setConnection((*(manager->getConnection()))); + devices.insert((*dit), d); + } + + authAgent = new TQProcess(authAgentExe, this); + authAgent->addArgument("--nofork"); + + TQFileInfo obexSrvExeFile( obexSrvExe ); + if (!obexSrvExeFile.exists()) + { + obexSrvExeFile.setFile(OBEX_EXE_ALT); + if (obexSrvExeFile.exists()) + { + m_config->writePathEntry("obexSrvExe", obexSrvExeFile.filePath()); + } + else + { + tqWarning( "obexd executable was not found\nSet path in configuration file \"%s\"\nVariable: obexSrvExe=", CONFIGURATION_FILE); + m_config->writeEntry("obexSrv", false); + } + } + + obexServer = new TQProcess(obexSrvExeFile.filePath(), this); + obexServer->addArgument("-n"); + obexServer->addArgument("-a"); + obexServer->addArgument("-l"); + obexServer->addArgument("-r"); + obexServer->addArgument(downloadDir); + + //stop tdebluezauth or obexd daemon to regain control + KUser user; + long uid = user.uid(); + + TQDir d("/proc"); + d.setFilter(TQDir::Dirs); + + TQRegExp rx( "^\\d+$" ); + for (int i = 0; i < d.count(); i++) + { + if ( ! rx.exactMatch(d[i]) ) + continue; + + TQFile f("/proc/" + d[i] + "/status"); + if ( ! f.open( IO_ReadOnly ) ) + { + tqDebug( "Failed to open file for reading: %s", f.name().local8Bit().data() ); + continue; + } + + TQTextStream stream( &f ); + TQString pid; + bool tokill = false; + while ( !stream.atEnd() ) { + TQString line = stream.readLine(); // line of text excluding '\n' + if (line.startsWith("Name:") && + ( line.endsWith("tdebluezauth") || line.endsWith("obexd") )) + { + pid = d[i]; + } + if (line.find(TQRegExp(TQString("Uid:\\s+%1").arg(uid))) != -1) + { + tokill = true; + } + } + f.close(); + + if (tokill && ! pid.isEmpty()) + { + if ( kill( (pid_t) pid.toLong(), SIGKILL ) == -1 ) + { + switch ( errno ) + { + case EINVAL: + tqDebug( "4\t%s", pid.local8Bit().data() ); + break; + case ESRCH: + tqDebug( "3\t%s", pid.local8Bit().data() ); + break; + case EPERM: + tqDebug( "2\t%s", pid.local8Bit().data() ); + break; + default: /* unknown error */ + tqDebug( "1\t%s", pid.local8Bit().data() ); + break; + } + } else { + tqWarning( "Cleanup pid\t%s", pid.local8Bit().data() ); + } + } + } + + // connect to manager signals + connect(manager, SIGNAL(adapterAdded(const TQString&)), TQT_SLOT(slotAdapterAdded(const TQString&))); + connect(manager, SIGNAL(adapterRemoved(const TQString&)), TQT_SLOT(slotAdapterRemoved(const TQString&))); + connect(manager, SIGNAL(deviceAdded(const TQString&)), TQT_SLOT(slotDeviceAdded(const TQString&))); + connect(manager, SIGNAL(deviceRemoved(const TQString&)), TQT_SLOT(slotDeviceRemoved(const TQString&))); +// connect(manager, SIGNAL(adapterPowerOnChanged(const TQString&, bool)), SLOT(slotPowerOnChanged(const TQString&, bool))); + + trayIcon = new TrayIcon(this); + setMainWidget(trayIcon); +} + +TDEBluetoothApp::~TDEBluetoothApp() +{ + + if (obexServer) + { + if (obexServer->isRunning()) + obexServer->kill(); + delete obexServer; + } + if (authAgent) + { + if (authAgent->isRunning()) + authAgent->kill(); + delete authAgent; + } + delete trayIcon; + + if (manager->isConnectedToDBUS()) + { + DevicesMap::Iterator dit = devices.begin(); + for (dit; dit != devices.end(); ++dit) + { + DeviceImpl *d = dit.data(); + if (d) + delete d; + } + devices.clear(); + + AdaptersMap::Iterator ait = adapters.begin(); + for (ait; ait != adapters.end(); ++ait) + { + AdapterImpl *a = ait.data(); + if (a) + { + TQT_DBusError error; + if (a->getDiscovering(error)) + a->StopDiscovery(error); + if (error.isValid()) + tqDebug("Stop discoverable for the adapter failed: %s", error.message().local8Bit().data()); + delete a; + } + } + adapters.clear(); + } + delete manager; + + if (m_config->isDirty()) + m_config->sync(); + + delete m_config; +} + +bool TDEBluetoothApp::startAuthAgent() +{ + if (!authAgent->isRunning()) + { + if (!authAgent->start()) + return false; + } + return true; +} + +bool TDEBluetoothApp::stopAuthAgent() +{ + if (authAgent->isRunning()) + { + authAgent->tryTerminate(); + TQTimer::singleShot(m_waitBeforeKill, authAgent, SLOT(kill())); + } + return true; +} + +bool TDEBluetoothApp::startObexSrv() +{ + if (!obexServer->isRunning()) + { + if (!obexServer->start()) + return false; + } + return true; +} + +bool TDEBluetoothApp::stopObexSrv() +{ + if (obexServer->isRunning()) + { + obexServer->tryTerminate(); + TQTimer::singleShot(m_waitBeforeKill, obexServer, SLOT(kill())); + } + return true; +} + +bool TDEBluetoothApp::isConnected() +{ + return manager->isConnectedToDBUS(); +} + +void TDEBluetoothApp::setAutoStart(bool val) +{ + if (val) + enableSessionManagement(); + else + disableSessionManagement(); + + m_config->setGroup("General"); + m_config->writeEntry("autoStart", val); +} + +void TDEBluetoothApp::setStartObex(bool val) +{ + m_config->setGroup("General"); + m_config->writeEntry("obexSrv", val); +} + +void TDEBluetoothApp::setStartAuthAgent(bool val) +{ + m_config->setGroup("General"); + m_config->writeEntry("authAgent", val); +} + +bool TDEBluetoothApp::getAutoStart() +{ + m_config->setGroup("General"); + return m_config->readBoolEntry("autoStart"); +} + +bool TDEBluetoothApp::getStartObex() +{ + m_config->setGroup("General"); + return m_config->readBoolEntry("obexSrv"); +} + +bool TDEBluetoothApp::getStartAuthAgent() +{ + m_config->setGroup("General"); + return m_config->readBoolEntry("authAgent"); +} + +void TDEBluetoothApp::slotAdapterAdded(const TQString &adapter) +{ + AdapterImpl *a = new AdapterImpl(BLUEZ_DN, adapter); + a->setConnection((*(manager->getConnection()))); + adapters.insert(adapter, a); + emit signalAdapterAdded(adapter); +} + +void TDEBluetoothApp::slotAdapterRemoved(const TQString &adapter) +{ + delete adapters[adapter]; + adapters.remove(adapter); + emit signalAdapterRemoved(adapter); +} + +void TDEBluetoothApp::slotDeviceAdded(const TQString &device) +{ + DeviceImpl *d = new DeviceImpl(BLUEZ_DN, device); + d->setConnection((*(manager->getConnection()))); + devices.insert(device, d); +} + +void TDEBluetoothApp::slotDeviceRemoved(const TQString &device) +{ + delete devices[device]; + devices.remove(device); +} + +#include "application.moc" diff --git a/src/tdebluez/application.h b/src/tdebluez/application.h new file mode 100644 index 0000000..84411ad --- /dev/null +++ b/src/tdebluez/application.h @@ -0,0 +1,97 @@ +/* + * + * New Bluetooth App for TDE and bluez5 + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of tdebluez. + * + * tdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * tdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef APPLICATION_H_ +#define APPLICATION_H_ + +#include +#include +#include + +#include + +class TDEConfig; +class TrayIcon; +class AdapterConfig; +class AdapterConfigDialog; +class DeviceWizard; + +using namespace TDEBluetooth; + +class TDEBluetoothApp: public KUniqueApplication +{ + Q_OBJECT + +public: + TDEBluetoothApp(); + ~TDEBluetoothApp(); + + typedef TQMap AdaptersMap; + typedef TQMap DevicesMap; + + bool startAuthAgent(); + bool stopAuthAgent(); + + bool startObexSrv(); + bool stopObexSrv(); + + bool isConnected(); + +protected: + ObjectManagerImpl *manager; + AdaptersMap adapters; + DevicesMap devices; + void setAutoStart(bool val); + void setStartObex(bool val); + void setStartAuthAgent(bool val); + bool getAutoStart(); + bool getStartObex(); + bool getStartAuthAgent(); + +private: + TQProcess *obexServer; + TQProcess *authAgent; + TrayIcon *trayIcon; + TDEConfig *m_config; + int m_waitBeforeKill; + + friend class TrayIcon; + friend class AdapterConfig; + friend class AdapterConfigDialog; + friend class DeviceWizard; + +public slots: + void slotAdapterAdded(const TQString& adapter); + void slotAdapterRemoved(const TQString& adapter); + // void slotPowerOnChanged(const TQString&, bool); + void slotDeviceAdded(const TQString& device); + void slotDeviceRemoved(const TQString& device); + +signals: + void signalAdapterAdded(const TQString &adapter); + void signalAdapterRemoved(const TQString &adapter); +}; + +#endif // APPLICATION_H_ diff --git a/src/tdebluez/devicedialog.ui b/src/tdebluez/devicedialog.ui new file mode 100644 index 0000000..cd640ef --- /dev/null +++ b/src/tdebluez/devicedialog.ui @@ -0,0 +1,335 @@ + +DeviceDialog +kseparator.h + + + DeviceDialog + + + + 0 + 0 + 550 + 190 + + + + Devices + + + + unnamed + + + + textLabel1_2 + + + + 0 + 0 + 0 + + + + <font size="+1">Configured Devices:</font> + + + + + layout6 + + + + unnamed + + + + + 1 + + + true + + + true + + + + + 2 + + + true + + + true + + + + + 3 + + + true + + + true + + + + + 4 + + + true + + + true + + + + deviceBox + + + GroupBoxPanel + + + Plain + + + 2 + + + Single + + + true + + + false + + + + + layout9 + + + + unnamed + + + + layout8 + + + + unnamed + + + + spacer5 + + + Horizontal + + + Fixed + + + + 40 + 20 + + + + + + layout7 + + + + unnamed + + + + pixmapLabel + + + + 0 + 0 + 0 + 0 + + + + + 48 + 50 + + + + + + + AlignCenter + + + + + spacer2 + + + Vertical + + + Expanding + + + + 20 + 40 + + + + + + + + spacer6 + + + Horizontal + + + Fixed + + + + 40 + 20 + + + + + + + + configureButton + + + + 130 + 28 + + + + Co&nfigure + + + Alt+N + + + + + connectButton + + + + 130 + 28 + + + + C&onnect + + + Alt+O + + + + + deleteButton + + + + 130 + 28 + + + + &Delete + + + Alt+D + + + + + + + + + kSeparator1 + + + + + lastLayout + + + + unnamed + + + + addButton + + + &Start Discovery << + + + Alt+S + + + true + + + + + okButton + + + &Close + + + Alt+C + + + + + spacer1 + + + Horizontal + + + Expanding + + + + 410 + 20 + + + + + + + + + + + + diff --git a/src/tdebluez/devicedialog_ext.ui b/src/tdebluez/devicedialog_ext.ui new file mode 100644 index 0000000..e251d09 --- /dev/null +++ b/src/tdebluez/devicedialog_ext.ui @@ -0,0 +1,155 @@ + +DeviceDialog_Ext + + + DeviceDialog_Ext + + + + 0 + 0 + 550 + 190 + + + + New Device + + + + unnamed + + + + newDevFrame + + + StyledPanel + + + Plain + + + 0 + + + + unnamed + + + + layout7 + + + + unnamed + + + + textLabel1 + + + <b>New Device:</b> + + + + + + Name + + + true + + + true + + + + + Address + + + true + + + true + + + + + 3 + + + false + + + true + + + + newdevList + + + true + + + + + + + layout4 + + + + unnamed + + + + setupButton + + + false + + + &Setup + + + Alt+S + + + + + spacer2 + + + Horizontal + + + Expanding + + + + 40 + 20 + + + + + + + + statusbar + + + + + + + + + + + devicedialog_ext.ui.h + + + diff --git a/src/tdebluez/devicesetupwizard.cpp b/src/tdebluez/devicesetupwizard.cpp new file mode 100644 index 0000000..77f88cf --- /dev/null +++ b/src/tdebluez/devicesetupwizard.cpp @@ -0,0 +1,662 @@ +/* + * + * Dialogs for tdebluez device configuration + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of tdebluez. + * + * tdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * tdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "application.h" +#include "devicesetupwizard.h" + +#define LOGOTIMEOUT 100 //100 msec +#define ASYNC_TIMEOUT 15000 //15 sec +#define CONNECT_TIMEOUT 5000 // 5 sec +#define PROGRESS_TIMEOUT 50 // 50 msec + +DeviceSetupWizard::DeviceSetupWizard(ObjectManagerImpl* _manager) : + DeviceSetupWizardDialog(), manager(_manager) +{ + device = 0; + address = TQString(); + + pairpage = page(0); + setHelpEnabled(pairpage, false); + + pairingpage = page(1); + setHelpEnabled(pairingpage, false); + + connectpage = page(2); + setHelpEnabled(connectpage, false); + + connectingpage = page(3); + setHelpEnabled(connectingpage, false); + + donepage = page(4); + setHelpEnabled(donepage, false); + setFinishEnabled(donepage, true); + cancelButton()->setText(i18n("S&kip Wizard")); + + setModal(true); + + m_config = TDEGlobal::config(); + + // create the first ListView + tQListViewSrc->setRootIsDecorated(TRUE); + tQListViewSrc->setSelectionMode(TQListView::Multi); + tQListViewSrc->clear(); + + // create the second ListView + tQListViewDst->setRootIsDecorated(TRUE); + tQListViewDst->setSelectionMode(TQListView::Multi); + tQListViewDst->clear(); + + // progress bars + pairingProgressBar->setProgress(0,ASYNC_TIMEOUT); + pairingProgressBar->setPercentageVisible(false); + connectingProgressBar->setProgress(0,ASYNC_TIMEOUT); + connectingProgressBar->setPercentageVisible(false); + + pairingTimer = new TQTimer(this); + connectTimer = new TQTimer(this); + connect(pairingTimer, SIGNAL(timeout()), this, TQT_SLOT(slotAdvancePairingProgressBar())); + connect(connectTimer, SIGNAL(timeout()), this, TQT_SLOT(slotAdvanceConnectProgressBar())); + + connect(manager, SIGNAL(deviceServicesResolvedChanged(const TQString&, bool)), + this, TQT_SLOT(slotDeviceServicesResolvedChanged(const TQString&, bool))); + + connect(buttonSrc2Dst, SIGNAL(clicked()), this, SLOT(slotCopySrc2Dst())); + connect(buttonDst2Src, SIGNAL(clicked()), this, SLOT(slotCopyDst2Src())); + connect(cancelPairingButton, SIGNAL(clicked()), this, SLOT(slotCancelPairing())); + connect(cancelConnectButton, SIGNAL(clicked()), this, SLOT(slotCancelConnecting())); +} + +DeviceSetupWizard::~DeviceSetupWizard() +{ +} + +void DeviceSetupWizard::next() +{ + if (pairingTimer->isActive()) + { + pairingTimer->stop(); + } + if (connectTimer->isActive()) + { + connectTimer->stop(); + } + + if (currentPage() == pairpage) + { + if (pairingRadioButton1->isChecked()) + { + pairingProgressBar->setProgress(0,ASYNC_TIMEOUT); + pairingTimer->start(PROGRESS_TIMEOUT); + setNextEnabled(pairpage, false); + setNextEnabled(pairingpage, false); + TQWizard::showPage(pairingpage); + startPairing(); + } + else + TQWizard::showPage(donepage); + } + else if (currentPage() == connectpage) + { + preferredProfiles.clear(); + TQListViewItemIterator it2(tQListViewDst); + while (it2.current()) + { + TQString selText = it2.current()->text(0); + for (TQStringList::iterator it3 = uuids.begin(); it3 != uuids.end(); + ++it3) + { + TQString u = (*it3); + if (selText == resolveUUID(u)) + { + kdDebug() << "REQUESTED UUID: " << u << endl; + preferredProfiles.append(u); + } + } + ++it2; + } + + m_config->setGroup(address); + m_config->writeEntry("profile", preferredProfiles); + m_config->sync(); + + // set the progress bar depending on the number of profiles to be connected + // and CONNECT_TIMEOUT value +// connectingProgressBar->setProgress(0, (ASYNC_TIMEOUT + CONNECT_TIMEOUT) * preferredProfiles.count()); + connectingProgressBar->setProgress(0,ASYNC_TIMEOUT); + + connectTimer->start(PROGRESS_TIMEOUT); + TQWizard::showPage(connectingpage); + + slotConnectNextProfile(); + } +// else if (currentPage() == connectingpage) +// { +// TQT_DBusError error; +// if (!device->getConnected(error)) +// { +// int asyncCallId=0; +// device->ConnectAsync(asyncCallId, error); +// manager->getConnection()->scheduleDispatch(); +// } +// else +// { +// TQWizard::next(); +// } +// if (error.isValid()) +// tqDebug("Failed in connecting device: %s", error.message().local8Bit().data()); +// } + else if (currentPage() == donepage) + { + if (trustedCheckBox->isChecked()) + { + finishButton()->setFocus(); + } + else + { + trustedCheckBox->setFocus(); + } + } +} + +void DeviceSetupWizard::back() +{ + TQWizard::back(); +} + +void DeviceSetupWizard::setDevice(DeviceImpl *_device) +{ + kdDebug() << "New device: " << _device << endl; + + if (device == _device) + return; + + if (device) + closeDevice(); + + device = _device; + + TQWizard::showPage(pairpage); + setNextEnabled(pairpage, true); + + TQT_DBusError error; + address = device->getAddress(error); + if (error.isValid()) + tqDebug("Failed to get address for the new device: %s", error.message().local8Bit().data()); + + if (device->getPaired(error)) + { + updateServiceList(); + preferredProfiles.clear(); + tQListViewDst->clear(); + m_config->setGroup(address); + preferredProfiles = m_config->readListEntry("profile"); + TQStringList::iterator it = preferredProfiles.begin(); + for (it; it != preferredProfiles.end(); ++it) + { + (void) new TQListViewItem(tQListViewDst, resolveUUID(*it)); + } + setAppropriate(pairpage, false); + if (tQListViewDst->childCount() > 0) + setNextEnabled(connectpage, true); + TQWizard::showPage(connectpage); + } else { + tQListViewDst->clear(); + } + if (error.isValid()) + tqDebug("Failed to get paired status for the new device: %s", error.message().local8Bit().data()); + + if (device->getConnected(error)) + { + setAppropriate(pairpage, false); + setAppropriate(pairingpage, false); + setAppropriate(connectpage, false); + setAppropriate(connectingpage, false); + TQWizard::showPage(donepage); + } + if (error.isValid()) + tqDebug("Failed to get connecting status of the new device: %s", error.message().local8Bit().data()); + + if (device->getTrusted(error)) + trustedCheckBox->setChecked(true); + if (error.isValid()) + tqDebug("Failed to get trusted status of the new device: %s", error.message().local8Bit().data()); + + connect(device, SIGNAL(PairAsyncReply(int /*asyncCallId*/)), + this, TQT_SLOT(slotPairAsyncReply(int /*asyncCallId*/))); + connect(device, SIGNAL(CancelPairingAsyncReply(int /*asyncCallId*/)), + this, TQT_SLOT(slotCancelPairingAsyncReply(int /*asyncCallId*/))); + connect(device, SIGNAL(AsyncErrorResponseDetected(int /*asyncCallId*/, const TQT_DBusError)), + this, TQT_SLOT(slotAsyncErrorResponseDetected(int /*asyncCallId*/, const TQT_DBusError))); + connect(device, SIGNAL(ConnectAsyncReply(int /*asyncCallId*/)), + this, TQT_SLOT(slotConnectAsyncReply(int /*asyncCallId*/))); +// connect(device, SIGNAL(DisonnectAsyncReply(int /*asyncCallId*/)), +// this, TQT_SLOT(slotDisconnectAsyncReply(int /*asyncCallId*/))); + connect(device, SIGNAL(ConnectProfileAsyncReply(int /*asyncCallId*/)), + this, TQT_SLOT(slotConnectProfileAsyncReply(int /*asyncCallId*/))); +// connect(device, SIGNAL(DisconnectProfileAsyncReply(int /*asyncCallId*/)), +// this, TQT_SLOT(slotDisconnectProfileAsyncReply(int /*asyncCallId*/))); +} + +void DeviceSetupWizard::updateServiceList() +{ + TQT_DBusError error; + uuids.clear(); + uuids = device->getUUIDs(error); + if (error.isValid()) + tqDebug("Failed to get uuids: %s", error.message().local8Bit().data()); + + tQListViewSrc->clear(); + for (TQStringList::iterator it = uuids.begin(); it != uuids.end(); ++it) + { + if ( + ((*it) == "00001203-0000-1000-8000-00805f9b34fb") || //Generic Audio + ((*it) == "00001108-0000-1000-8000-00805f9b34fb") || //Headset + ((*it) == "0000111e-0000-1000-8000-00805f9b34fb") || //Handsfree + ((*it) == "0000111f-0000-1000-8000-00805f9b34fb") || //Handsfree AG + ((*it) == "0000110a-0000-1000-8000-00805f9b34fb") || //A2DP Source + ((*it) == "0000110b-0000-1000-8000-00805f9b34fb") || //A2DP Sink + ((*it) == "00001103-0000-1000-8000-00805f9b34fb") || //DUN Gateway + ((*it) == "00001800-0000-1000-8000-00805f9b34fb") //GAP + ) + { + (void) new TQListViewItem(tQListViewSrc, resolveUUID((*it))); + } + } +} + +void DeviceSetupWizard::startPairing() +{ + TQT_DBusError error; + int asyncCallId = 0; + if (!device->PairAsync(asyncCallId, error)) + { + if (error.isValid()) + tqDebug("Failed to get paired status for the new device: %s", error.message().local8Bit().data()); + } + manager->getConnection()->scheduleDispatch(); +} + +void DeviceSetupWizard::slotPairingTimeOut() +{ + if(pairingTimer->isActive()) + pairingTimer->stop(); + + if (!device) + return; + + TQT_DBusError error; + if (!device->getPaired(error)) + { + if (!error.isValid()) + { + TQWizard::showPage(pairpage); + setNextEnabled(pairpage, true); + } + else + tqDebug("Failed pairing the new device: %s", error.message().local8Bit().data()); + } + else + { + if (tQListViewDst->childCount() > 0) + setNextEnabled(connectpage, true); + TQWizard::showPage(connectpage); + } +} + +void DeviceSetupWizard::slotConnectTimeOut() +{ + if(connectTimer->isActive()) + connectTimer->stop(); + + if (!device) + return; + + TQT_DBusError error; + if (!device->getConnected(error)) + { + if (!error.isValid()) + { + TQWizard::showPage(connectpage); + if (tQListViewDst->childCount() > 0) + setNextEnabled(connectpage, true); + setNextEnabled(connectpage, true); + } + else + tqDebug("Failed connecting the new device: %s", error.message().local8Bit().data()); + } + else + { + setNextEnabled(connectingpage, false); + setBackEnabled(donepage, false); + TQWizard::showPage(donepage); + } +} + +/** the cancel button is connected to the reject() slot of TQDialog, + * so we have to reimplement this here to add a dialogbox to ask if we + * really want to quit the wizard. + */ +void DeviceSetupWizard::reject() +{ + close(); // this will trigger the close event caught below +} + +void DeviceSetupWizard::closeEvent(TQCloseEvent* e) +{ + if (askClose()) + { + hide(); + closeDevice(); + } + else + { + e->ignore(); + } +} + +/** maybe call a dialog that the wizard has finished. */ +void DeviceSetupWizard::accept() +{ + TQT_DBusError error; + if (trustedCheckBox->isChecked()) + { + if (!device->getTrusted(error)) + { + device->setTrusted(true, error); + } + if (error.isValid()) + tqDebug("Could not set trusted for %s\nError: %s", + address.latin1(), error.message().local8Bit().data()); + } + + hide(); + closeDevice(); +} + +void DeviceSetupWizard::closeDevice() +{ +// make sure timers go down + if (pairingTimer->isActive()) + { + pairingTimer->stop(); + } + if (connectTimer->isActive()) + { + connectTimer->stop(); + } + + if (!device) + return; + + disconnect(device, SIGNAL(PairAsyncReply(int /*asyncCallId*/)), + this, TQT_SLOT(slotPairAsyncReply(int /*asyncCallId*/))); + disconnect(device, SIGNAL(CancelPairingAsyncReply(int /*asyncCallId*/)), + this, TQT_SLOT(slotCancelPairingAsyncReply(int /*asyncCallId*/))); + disconnect(device, SIGNAL(AsyncErrorResponseDetected(int /*asyncCallId*/, const TQT_DBusError)), + this, TQT_SLOT(slotAsyncErrorResponseDetected(int /*asyncCallId*/, const TQT_DBusError))); + disconnect(device, SIGNAL(ConnectAsyncReply(int /*asyncCallId*/)), + this, TQT_SLOT(slotConnectAsyncReply(int /*asyncCallId*/))); +// disconnect(device, SIGNAL(DisonnectAsyncReply(int /*asyncCallId*/)), +// this, TQT_SLOT(slotDisconnectAsyncReply(int /*asyncCallId*/))); + disconnect(device, SIGNAL(ConnectProfileAsyncReply(int /*asyncCallId*/)), + this, TQT_SLOT(slotConnectProfileAsyncReply(int /*asyncCallId*/))); +// connect(device, SIGNAL(DisconnectProfileAsyncReply(int /*asyncCallId*/)), +// this, TQT_SLOT(slotDisconnectProfileAsyncReply(int /*asyncCallId*/))); + + preferredProfiles.clear(); + address = TQString(); + device = 0; +} + +void DeviceSetupWizard::slotDeviceServicesResolvedChanged(const TQString &path, bool resolved) +{ + if (!device) + return; + + if (path != device->getPath()) + return; + + updateServiceList(); +} + +void DeviceSetupWizard::slotConnectNextProfile() +{ + if (preferredProfiles.isEmpty()) + { + slotConnectTimeOut(); + } + else + { + TQString connect = preferredProfiles.first(); + //disable next button while profiles are being handled + setBackEnabled(connectpage, false); + setNextEnabled(connectpage, false); + setBackEnabled(connectingpage, false); + setNextEnabled(connectingpage, false); + int asyncCallId = 0; + TQT_DBusError error; + if (!device->ConnectProfileAsync(asyncCallId, connect, error)) + { + if (error.isValid()) + tqDebug("Failed to call DBus ConnectProfileAsync: %s", error.message().local8Bit().data()); + } + manager->getConnection()->scheduleDispatch(); + } +} + +void DeviceSetupWizard::slotAsyncErrorResponseDetected(int asyncCallId, const TQT_DBusError error) +{ + tqDebug("AsyncErrorResponseDetected: %i %s %s", error.type(), error.name().local8Bit().data(), error.message().local8Bit().data()); + + if(pairingTimer->isActive()) + pairingTimer->stop(); + + if(connectTimer->isActive()) + connectTimer->stop(); + + switch (error.type()) + { + case 5: //org.freedesktop.DBus.Error.NoReply + case 22: // org.bluez.Error.InProgress, org.bluez.Error.Failed Host is down + default: + if (currentPage() == pairingpage) + { + slotPairingTimeOut(); + } + if (currentPage() == connectingpage) + { + slotConnectTimeOut(); + } + } +// TQMessageBox::critical( 0, "Device Setup Wizard", +// TQString("AsyncErrorResponseDetected: %1\n%2\n%3") +// .arg(error.type()) +// .arg(error.name().local8Bit().data()) +// .arg(error.message().local8Bit().data()), +// TQMessageBox::Ok, TQMessageBox::NoButton, TQMessageBox::NoButton); + + KNotifyClient::event(TDEApplication::kApplication()->mainWidget()->winId(), + "ConnectionError", tr("AsyncErrorResponseDetected: %1\n%2\n%3") + .arg(error.type()) + .arg(error.name().local8Bit().data()) + .arg(error.message().local8Bit().data())); +} + +void DeviceSetupWizard::slotConnectAsyncReply(int asyncCallId) +{ + slotConnectTimeOut(); +} + +//void DeviceSetupWizard::slotDisconnectAsyncReply(int asyncCallId) +//{ +// slotConnectNextProfile(); +//} + +void DeviceSetupWizard::slotConnectProfileAsyncReply(int asyncCallId) +{ + kdDebug() << __func__ << endl; + if (!preferredProfiles.isEmpty()) + preferredProfiles.pop_front(); + + if (!preferredProfiles.isEmpty() && connectTimer->isActive()) + // delay connecting next profile to prevent error InProgress + TQTimer::singleShot(CONNECT_TIMEOUT, this, TQT_SLOT(slotConnectNextProfile())); + else + slotConnectTimeOut(); +} + +//void DeviceSetupWizard::slotDisconnectProfileAsyncReply(int asyncCallId) +//{ +// slotConnectTimeOut(); +//} + +void DeviceSetupWizard::slotPairAsyncReply(int asyncCallId) +{ + slotPairingTimeOut(); +} + +void DeviceSetupWizard::slotCancelPairingAsyncReply(int asyncCallId) +{ + slotPairingTimeOut(); +} + +void DeviceSetupWizard::slotCancelPairing() +{ + int asyncCallId = 0; + TQT_DBusError error; + if (!device->CancelPairingAsync(asyncCallId, error)) + { + if (error.isValid()) + tqDebug("Failed to call DBus CancelPairingAsync: %s", error.message().local8Bit().data()); + } + + if(pairingTimer->isActive()) + pairingTimer->stop(); +} + +void DeviceSetupWizard::slotCancelConnecting() +{ + int asyncCallId = 0; + TQT_DBusError error; + if (device->getConnected(error)) + { + if (!device->DisconnectAsync(asyncCallId, error)) + tqDebug("Failed to call DisconnectAsync: %s", error.message().local8Bit().data()); + } + if (error.isValid()) + tqDebug("Failed in slotCancelConnecting: %s", error.message().local8Bit().data()); + + if(connectTimer->isActive()) + connectTimer->stop(); +} + +void DeviceSetupWizard::slotAdvancePairingProgressBar() +{ + if (pairingProgressBar->progress() < pairingProgressBar->totalSteps()) + { + pairingProgressBar->setProgress(pairingProgressBar->progress() + ASYNC_TIMEOUT/PROGRESS_TIMEOUT); + } + else + pairingProgressBar->setProgress(0,ASYNC_TIMEOUT); +} + +void DeviceSetupWizard::slotAdvanceConnectProgressBar() +{ + if (connectingProgressBar->progress() < connectingProgressBar->totalSteps()) + { + connectingProgressBar->setProgress(connectingProgressBar->progress() + ASYNC_TIMEOUT/PROGRESS_TIMEOUT); + } + else + connectingProgressBar->setProgress(0,ASYNC_TIMEOUT); +} + +void DeviceSetupWizard::slotNext() +{ + TQWizard::next(); +} + +bool DeviceSetupWizard::askClose() +{ + TQString text; + if (currentPage() == page(0)) + { + text = i18n("

Are you sure you want to quit the Device Settings Wizard?

" + "

The Device Settings Wizard helps you to configure the BT device and use it later.

" + "

Click Cancel to return and finish your setup.

"); + } + else + { + text = i18n("

Are you sure you want to quit the Device Settings Wizard?

" + "

If yes, click Quit and all changes will be lost." + "
If not, click Cancel to return and finish your setup.

"); + } + int status = KMessageBox::warningContinueCancel(this, text, i18n("All Changes Will Be Lost"), KStdGuiItem::quit()); + if (status == KMessageBox::Continue) + return true; + else + return false; +} + +void DeviceSetupWizard::slotCopySrc2Dst() +{ + tQListViewDst->clear(); + // iterate through the first ListView... + TQListViewItemIterator it(tQListViewSrc, TQListViewItemIterator::Selected); + while (it.current()) + { + (void) new TQListViewItem(tQListViewDst, it.current()->text(0)); + ++it; + } + if (tQListViewDst->childCount() > 0) + setNextEnabled(connectpage, true); +} + +void DeviceSetupWizard::slotCopyDst2Src() +{ + // iterate through the first ListView... + TQListViewItemIterator it(tQListViewDst, TQListViewItemIterator::Selected); + while (it.current()) + { + TQListViewItem *item = it.current(); + ++it; + delete item; + } + if (tQListViewDst->childCount() == 0) + setNextEnabled(connectpage, false); +} + +#include "devicesetupwizard.moc" diff --git a/src/tdebluez/devicesetupwizard.h b/src/tdebluez/devicesetupwizard.h new file mode 100644 index 0000000..c75e839 --- /dev/null +++ b/src/tdebluez/devicesetupwizard.h @@ -0,0 +1,119 @@ +/* + * + * Dialogs for tdebluez devices + * + * Copyright (C) 2007 Tom Patzig + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of tdebluez. + * + * kbluetooth is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * kbluetooth is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#ifndef DEVICESETUPWIZARD_H +#define DEVICESETUPWIZARD_H + +#include + +#include "devicesetupwizarddialog.h" + +using namespace TDEBluetooth; + +class DeviceSetupWizard: public DeviceSetupWizardDialog +{ + Q_OBJECT + +public: + DeviceSetupWizard(ObjectManagerImpl *manager); + ~DeviceSetupWizard(); + + virtual void next(); + virtual void back(); + void setDevice(DeviceImpl *device); + +private: + void updateServiceList(); + void startPairing(); + +public slots: // Public slots + /** the cancel button is connected to the reject() slot of TQDialog, + * so we have to reimplement this here to add a dialogbox to + * ask if we really want to quit the wizard. + */ + void reject(); + /** maybe call a dialog that the wizard has finished. + * Calls applySettings() to save the current selection. + */ + void accept(); + void closeDevice(); + + /** We need this to use it in a TQTimer */ + void slotNext(); + +private slots: + // from object manager + void slotDeviceServicesResolvedChanged(const TQString &path, bool resolved); + + // local + void slotPairingTimeOut(); + void slotConnectTimeOut(); + void slotConnectNextProfile(); + + // from device1Proxy + void slotAsyncErrorResponseDetected(int asyncCallId, const TQT_DBusError error); + void slotConnectAsyncReply(int asyncCallId); +// void slotDisconnectAsyncReply(int asyncCallId); + void slotConnectProfileAsyncReply(int asyncCallId); +// void slotDisconnectProfileAsyncReply(int asyncCallId); + void slotPairAsyncReply(int asyncCallId); + void slotCancelPairingAsyncReply(int asyncCallId); + void slotCancelPairing(); + void slotCancelConnecting(); + void slotAdvancePairingProgressBar(); + void slotAdvanceConnectProgressBar(); + +protected slots: + void slotCopySrc2Dst(); + void slotCopyDst2Src(); + +protected: + // Protected methods + // the close button on the titlebar sets e->accept() which we don't want. + virtual void closeEvent(TQCloseEvent*); + bool askClose(); + +private: + ObjectManagerImpl *manager; +// AdapterImpl *adapter; + DeviceImpl *device; + TQString address; + TQStringList uuids; + + TQWidget* pairpage; + TQWidget* pairingpage; + TQWidget* connectpage; + TQWidget* connectingpage; + TQWidget* donepage; + TQTimer* pairingTimer; + TQTimer* connectTimer; + +// TQMap asyncCalls; + + TQStringList preferredProfiles; + TDEConfig* m_config; +}; + +#endif diff --git a/src/tdebluez/devicesetupwizarddialog.ui b/src/tdebluez/devicesetupwizarddialog.ui new file mode 100644 index 0000000..dfaa00b --- /dev/null +++ b/src/tdebluez/devicesetupwizarddialog.ui @@ -0,0 +1,403 @@ + +DeviceSetupWizardDialog + + + DeviceSetupWizardDialog + + + + 0 + 0 + 515 + 368 + + + + Device Setup Wizard + + + + WizardPage + + + Pair + + + + pairingButtonGroup + + + true + + + + 30 + 60 + 350 + 110 + + + + Device pairing + + + + pairingRadioButton2 + + + + 10 + 70 + 220 + 31 + + + + Pair &device later + + + Alt+D + + + + + pairingRadioButton1 + + + + 10 + 30 + 210 + 30 + + + + Pair de&vice now + + + Alt+V + + + true + + + + + + pairingTextLabel + + + + 30 + 20 + 350 + 30 + + + + Do you want to pair device now or later? + + + + + + WizardPage + + + Pairing + + + + progressBarFrame1 + + + + 30 + 10 + 420 + 50 + + + + StyledPanel + + + Raised + + + + pairingProgressBar + + + + 5 + 9 + 300 + 29 + + + + + + cancelPairingButton + + + + 315 + 9 + 91 + 31 + + + + &Cancel + + + Alt+C + + + + + + + WizardPage + + + Connect + + + + buttonSrc2Dst + + + + 204 + 62 + 51 + 31 + + + + &>> + + + Alt+> + + + + + buttonDst2Src + + + + 204 + 102 + 51 + 31 + + + + NoFocus + + + &<< + + + Alt+< + + + + + + Service + + + true + + + true + + + + + Neuer Eintrag + + + + + + + tQListViewSrc + + + + 0 + 30 + 190 + 240 + + + + + + + Service + + + true + + + true + + + + + Neuer Eintrag + + + + + + + tQListViewDst + + + + 270 + 30 + 201 + 240 + + + + + + connectTextLabel + + + + 0 + 0 + 270 + 30 + + + + Select service to connect: + + + + + + WizardPage + + + Connecting + + + + progressBarFrame2 + + + + 40 + 20 + 420 + 50 + + + + StyledPanel + + + Raised + + + + connectingProgressBar + + + + 5 + 9 + 300 + 29 + + + + + + cancelConnectButton + + + + 315 + 9 + 91 + 31 + + + + &Cancel + + + Alt+C + + + + + + + WizardPage + + + Done + + + + doneTextLabel + + + + 30 + 20 + 261 + 31 + + + + PaletteBackground + + + WidgetOrigin + + + Done setup device + + + + + trustedCheckBox + + + + 30 + 60 + 270 + 31 + + + + Trust the &device + + + Alt+D + + + + + + kcombobox.h + + + diff --git a/src/tdebluez/devicewizard.cpp b/src/tdebluez/devicewizard.cpp new file mode 100644 index 0000000..51be413 --- /dev/null +++ b/src/tdebluez/devicewizard.cpp @@ -0,0 +1,866 @@ +/* + * + * Device Manager Gui for tdebluez + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of tdebluez. + * + * tdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * tdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "application.h" +#include "devicewizard.h" + +#define LOGOTIMEOUT 80 //80 msec +#define CONTIMEOUT 5000 //5 sec + +DeviceWizard::DeviceWizard(TDEBluetoothApp* a) : + DeviceDialog(), app(a) +{ + // use the first powered adapter + TQMap::Iterator ait = app->adapters.begin(); + for (ait; ait != app->adapters.end(); ++ait) + { + AdapterImpl *adptr = ait.data(); + if (adptr) + { + TQT_DBusError dbuserr; + if (adptr->getPowered(dbuserr)) + { + adapter = ait.data(); + break; + } + if (dbuserr.isValid()) + tqDebug("Get powered for active adapter failed: %s", dbuserr.message().local8Bit().data()); + } + } + + // else use the first available + if (!adapter) + adapter = (app->adapters).begin().data(); + + devicesetupwizard = new DeviceSetupWizard(app->manager); + + devicedlg_ext = new DeviceDialog_Ext(this); + setExtension(devicedlg_ext->newDevFrame); + setOrientation(TQt::Vertical); + + mainlogo = TDEGlobal::iconLoader()->loadIcon("tdebluez", TDEIcon::Small, 16); + setIcon(mainlogo); + + connectingDevice = {ConState::IDLE, TQString(), TQStringList()}; + + pix = TDEGlobal::iconLoader()->loadIcon("bluetooth", TDEIcon::Small, 48); + pixmapLabel->setPixmap(pix); + pixmapLabel->show(); + + logoBlend = pix; + KPixmapEffect::blend(logoBlend, -1, red, KPixmapEffect::DiagonalGradient); + + deviceBox->header()->hide(); + + // add devices + TDEBluetoothApp::DevicesMap::Iterator dit = app->devices.begin(); + for (dit; dit != app->devices.end(); ++dit) + { + kdDebug() << "device path: " << dit.key() << endl; + slotInsertDevice(dit.key()); + } + + int colSize = deviceBox->columnWidth(1); + deviceBox->setColumnWidth(1, colSize + 20); + + /* disable button by default */ + setStateDeviceButtons(false); + + devicedlg_ext->newdevList->header()->hide(); + devicedlg_ext->newdevList->setColumnAlignment(2, TQt::AlignVCenter); + devicedlg_ext->setupButton->setEnabled(false); + + // connect signals + // devicedlg_ext + connect(devicedlg_ext->setupButton, SIGNAL(clicked()), + this, TQT_SLOT(slotSetupNewDevice())); + connect(devicedlg_ext->newdevList, SIGNAL(clicked(TQListViewItem*)), + this, TQT_SLOT(slotChangeSetupButton(TQListViewItem*))); + + // deviceBox + connect(deviceBox, SIGNAL(clicked(TQListViewItem*)), + this, TQT_SLOT(slotDeviceBoxClicked(TQListViewItem*))); + + // this wizard + connect(addButton, SIGNAL(toggled(bool)), + this, TQT_SLOT(showExtension(bool))); + connect(addButton, SIGNAL(toggled(bool)), + this, TQT_SLOT(slotSearch(bool))); + + connect(okButton, SIGNAL(clicked()), + this, SLOT(slotCloseDialog())); + + connect(configureButton, SIGNAL(clicked()), + this, TQT_SLOT(slotConfigDevice())); + connect(connectButton, SIGNAL(clicked()), + this, TQT_SLOT(slotConnectButton())); + connect(deleteButton, SIGNAL(clicked()), + this, TQT_SLOT(slotDeleteDevice())); + + // ADAPTER -> DIALOG + connect(app->manager, SIGNAL(adapterDiscoveringChanged(const TQString&, bool)), + this, TQT_SLOT(slotAdapterDiscoveringChanged(const TQString&, bool))); + + // DEVICE -> DIALOG + connect(app->manager, SIGNAL(deviceConnectedChanged(const TQString&, bool)), + this, TQT_SLOT(slotDeviceConnectedChanged(const TQString&, bool))); + connect(app->manager, SIGNAL(deviceAdded(const TQString&)), + this, TQT_SLOT(slotInsertDevice(const TQString&))); + connect(app->manager, SIGNAL(deviceRemoved(const TQString&)), + this, TQT_SLOT(slotDeviceRemoved(const TQString&))); + connect(app->manager, SIGNAL(deviceNameChanged(const TQString&, const TQString&)), + this, TQT_SLOT(slotDeviceNameChanged(const TQString&, const TQString&))); + connect(app->manager, SIGNAL(deviceAliasChanged(const TQString&, const TQString&)), + this, TQT_SLOT(slotDeviceNameChanged(const TQString&, const TQString&))); + connect(app->manager, SIGNAL(devicePairedChanged(const TQString&,bool)), + this, TQT_SLOT(slotDevicePairedChanged(const TQString&,bool))); + + // connect(app->manager, SIGNAL(mediaControlConnectedChanged(const TQString&,bool)), + // TQT_SLOT(slotMediaControlConnectedChanged(const TQString&,bool))); + + chgLogoTimer = new TQTimer(this); + TQObject::connect(chgLogoTimer, SIGNAL(timeout()), this, SLOT(slotUpdateLogo())); + timer = false; +} + +DeviceWizard::~DeviceWizard() +{ +// if (mediaCtlDialog) +// delete mediaCtlDialog; + delete devicedlg_ext; + delete devicesetupwizard; +} + +void DeviceWizard::slotCloseDialog() +{ + close(); +} + +void DeviceWizard::slotDeviceBoxClicked(TQListViewItem *dev) +{ + if (!dev) + { + setStateDeviceButtons(true); + return; + } + + TQString selAddr = dev->text(2); + if (connectingDevice.state == ConState::CONNECTING) + { + if (selAddr == connectingDevice.address) + { + connectButton->setText(i18n("&Disconnect")); + connectButton->setEnabled(true); + } + else + { + setStateDeviceButtons(false); + TQMessageBox::information(this, + i18n("Trying to connect device: ") + connectingDevice.address, + i18n("Either wait or try disconnecting first"), + TQMessageBox::Ok | TQMessageBox::Default, + TQMessageBox::NoButton, + TQMessageBox::NoButton); + } + return; + } + + + TDEBluetoothApp::DevicesMap::Iterator dit = app->devices.begin(); + for (dit; dit != app->devices.end(); ++dit) + { + TQT_DBusError dbuserr; + TQString addr = dit.data()->getAddress(dbuserr); + if (dbuserr.isValid()) + tqDebug("Device getAddress failed: %s", dbuserr.message().local8Bit().data()); + if (selAddr == addr) + { + bool connected = dit.data()->getConnected(dbuserr); + if (dbuserr.isValid()) + tqDebug("Device getConnected failed: %s", dbuserr.message().local8Bit().data()); + if (connected) + { + connectButton->setText(i18n("&Disconnect")); + } + else + { + connectButton->setText(i18n("C&onnect")); + } + break; + } + } + setStateDeviceButtons(true); +} + +void DeviceWizard::slotChangeSetupButton(TQListViewItem *dev) +{ + devicedlg_ext->setupButton->setEnabled(true); +} + +void DeviceWizard::slotConnectButton() +{ + TQListViewItem *sel = deviceBox->selectedItem(); + + if (!sel) + return; + + setStateDeviceButtons(false); + + TDEBluetoothApp::DevicesMap::Iterator dit = app->devices.begin(); + for (dit; dit != app->devices.end(); ++dit) + { + TQT_DBusError dbuserr; + TQString addr = dit.data()->getAddress(dbuserr); + if (dbuserr.isValid()) + tqDebug("Device getAddress failed: %s", dbuserr.message().local8Bit().data()); + if (sel->text(2) == addr) + { + if (connectButton->text() == "C&onnect") + { + connectingDevice.state = ConState::CONNECTING; + connectingDevice.address = addr; + + app->m_config->setGroup(addr); + TQStringList profiles = app->m_config->readListEntry("profile"); + connectingDevice.profiles = profiles; + + if (connectingDevice.profiles.isEmpty()) + { + devicesetupwizard->setDevice(dit.data()); + devicesetupwizard->show(); + devicesetupwizard->raise(); + devicedlg_ext->setupButton->setEnabled(false); + } + else + { + slotConnectNextProfile(); + } + } + else + { + int asyncCallId = 0; + if (dit.data()->DisconnectAsync(asyncCallId, dbuserr)) + { + app->manager->getConnection()->scheduleDispatch(); + connectingDevice.state = ConState::DISCONNECTING; + connectingDevice.profiles.clear(); + } + if (dbuserr.isValid()) + { + tqDebug("DisconnectAsync failed: %s", dbuserr.message().local8Bit().data()); + } + } + break; + } + } +} + +void DeviceWizard::slotConnectNextProfile() +{ + TQString profile = connectingDevice.profiles.first(); + if (profile.isEmpty()) + { + connectingDevice = {ConState::IDLE, TQString(), TQStringList()}; + return; + } + + TQT_DBusError dbuserr; + TQString path; + TDEBluetoothApp::DevicesMap::Iterator dit = app->devices.begin(); + for (dit; dit != app->devices.end(); ++dit) + { + TQString addr = dit.data()->getAddress(dbuserr); + if (dbuserr.isValid()) + tqDebug("Device getAddress failed: %s", dbuserr.message().local8Bit().data()); + if (addr == connectingDevice.address) + { + path = dit.key(); + break; + } + } + + if (path.isEmpty()) + { + connectingDevice = {ConState::IDLE, TQString(), TQStringList()}; + return; + } + + int asyncCallId = 0; + if (app->devices[path]->ConnectProfileAsync(asyncCallId, profile, dbuserr)) + { + app->manager->getConnection()->scheduleDispatch(); + + connectingDevice.profiles.pop_front(); + if (!connectingDevice.profiles.isEmpty()) + TQTimer::singleShot(CONTIMEOUT, this, TQT_SLOT(slotConnectNextProfile())); + } + else + { + if (dbuserr.isValid()) + tqDebug("ConnectProfileAsync failed: %s", dbuserr.message().local8Bit().data()); + + TQString text = i18n("

Attempt to start connection with the device failed

" + "

You can retry to connect or click Cancel to stop.

"); + int status = KMessageBox::warningContinueCancel(this, + text, + i18n("Connection attempts will be interrupted"), + KStdGuiItem::quit()); + if (status == KMessageBox::Continue) + TQTimer::singleShot(CONTIMEOUT, this, TQT_SLOT(slotConnectNextProfile())); + else + connectingDevice = {ConState::IDLE, TQString(), TQStringList()}; + } +} + +void DeviceWizard::slotSetAdapter(const TQString &path, const TQString &name) +{ + adapter = app->adapters[path]; +} + +void DeviceWizard::setStateDeviceButtons(bool state) +{ + connectButton->setEnabled(state); + deleteButton->setEnabled(state); + configureButton->setEnabled(state); +} + +void DeviceWizard::slotDeviceConnectedChanged(const TQString &path, bool connect) +{ + TQT_DBusError dbuserr; + TQString addr = app->devices[path]->getAddress(dbuserr); + if (dbuserr.isValid()) + tqDebug("Device getAddress failed: %s", dbuserr.message().local8Bit().data()); + + TQListViewItem *devItem = deviceBox->findItem(addr, 2, TQt::ExactMatch); + if (!devItem) + return; // may be it was already deleted + + //this is the selected item + if (devItem == deviceBox->selectedItem()) + { + if (connect) + connectButton->setText(i18n("&Disconnect")); + else + connectButton->setText(i18n("C&onnect")); + setStateDeviceButtons(true); + } + + if (connect) + { + KPixmap pix = TDEGlobal::iconLoader()->loadIcon("bookmark", TDEIcon::Small, 16); + devItem->setPixmap(0, pix); + } + else + { + devItem->setPixmap(0, TQPixmap()); + } + + // If we initiated the connection request, set state + if (addr == connectingDevice.address) + { + switch (connectingDevice.state) + { + case ConState::CONNECTING: + if (connectingDevice.profiles.isEmpty()) + connectingDevice = {ConState::IDLE, TQString(), TQStringList()}; + break; + case ConState::DISCONNECTING: + connectingDevice = {ConState::IDLE, TQString(), TQStringList()}; + break; + } + } +} + +void DeviceWizard::slotDeleteDevice() +{ + TQListViewItem *sel = deviceBox->selectedItem(); + /* No device selected */ + if (!sel) + return; + + TDEBluetoothApp::DevicesMap::Iterator dit = app->devices.begin(); + for (dit; dit != app->devices.end(); ++dit) + { + TQT_DBusError dbuserr; + TQString addr = dit.data()->getAddress(dbuserr); + if (dbuserr.isValid()) + tqDebug("Device getAddress failed: %s", dbuserr.message().local8Bit().data()); + if (sel->text(2) == addr) + { + if (!adapter->RemoveDevice(TQT_DBusObjectPath(dit.key()), dbuserr)) + { + TQString err = (dbuserr.isValid()) ? dbuserr.message() : i18n("No error message"); + TQMessageBox::information(this, + i18n("Remove failed"), + i18n("Device ") + addr + i18n(" could not be removed!\nReason: ") + err, + TQMessageBox::Ok | TQMessageBox::Default, + TQMessageBox::NoButton, + TQMessageBox::NoButton); + } + + app->m_config->deleteGroup(addr); + app->m_config->sync(); + + break; + } + } + // fix for not emitting signal after device was remove + // this results in not updating the device wizard and + // leaving the device in the list, while the device is + // removed from the adapter list + app->manager->getConnection()->scheduleDispatch(); + // selection is to be removed + setStateDeviceButtons(false); +} + +void DeviceWizard::slotDeviceRemoved(const TQString& path) +{ + kdDebug() << __func__ << endl; + // because device was already deleted from the devicemap + // we need to find out which view item it is + TQStringList addrList1 = TQStringList(); + TQStringList addrList2 = TQStringList(); + + TQListViewItemIterator it1(deviceBox); + while (it1.current()) + { + TQString addr = it1.current()->text(2); + addrList1.append(addr); + ++it1; + } + + TQListViewItemIterator it2(devicedlg_ext->newdevList); + while (it2.current()) + { + TQString addr = it2.current()->text(1); + addrList2.append(addr); + ++it2; + } + + TDEBluetoothApp::DevicesMap::Iterator dit = app->devices.begin(); + for (dit; dit != app->devices.end(); ++dit) + { + TQT_DBusError dbuserr; + TQString addr = dit.data()->getAddress(dbuserr); + if (dbuserr.isValid()) + tqDebug("Device getAddress failed: %s", dbuserr.message().local8Bit().data()); + if (addrList1.grep(addr).size() == 1) + addrList1.remove(addr); + if (addrList2.grep(addr).size() == 1) + addrList2.remove(addr); + } + for (TQStringList::Iterator it = addrList1.begin(); it != addrList1.end(); + ++it) + { + TQListViewItem *item = deviceBox->findItem((*it), 2, TQt::ExactMatch); + if (item) + delete item; + } + for (TQStringList::Iterator it = addrList2.begin(); it != addrList2.end(); + ++it) + { + TQListViewItem *item = devicedlg_ext->newdevList->findItem((*it), 1, TQt::ExactMatch); + if (item) + delete item; + } + + devicedlg_ext->setupButton->setEnabled(false); + setStateDeviceButtons(false); +} + +void DeviceWizard::slotConfigDevice() +{ + TQListViewItem *sel = deviceBox->selectedItem(); + // No device selected + if (!sel) + return; + + TDEBluetoothApp::DevicesMap::Iterator dit = app->devices.begin(); + for (dit; dit != app->devices.end(); ++dit) + { + TQT_DBusError dbuserr; + TQString addr = dit.data()->getAddress(dbuserr); + if (dbuserr.isValid()) + tqDebug("Device getAddress failed: %s", dbuserr.message().local8Bit().data()); + if (sel->text(2) == addr) + { + // device to be configured + setStateDeviceButtons(false); + devicesetupwizard->setDevice(dit.data()); + devicesetupwizard->show(); + devicesetupwizard->raise(); + devicedlg_ext->setupButton->setEnabled(false); + kdDebug() << "address: _____________" << addr << endl; + break; + } + } +} + +void DeviceWizard::slotSearch(bool state) +{ + // Discovering can be enabled and disabled only from + // the one and the same application + TQT_DBusError dbuserr; + if (state) + { + adapter->StartDiscovery(dbuserr); + if (dbuserr.isValid()) + tqDebug("Device StartDiscovery failed: %s", dbuserr.message().local8Bit().data()); + addButton->setText(i18n("&Stop Discovery >>")); + } + else + { + adapter->StopDiscovery(dbuserr); + if (dbuserr.isValid()) + tqDebug("Device StopDiscovery failed: %s", dbuserr.message().local8Bit().data()); + addButton->setText(i18n("&Start Discovery <<")); + } +} + +void DeviceWizard::slotAdapterDiscoveringChanged(const TQString& path, bool state) +{ + timer = state; + + if (state) + { +// new device list should be empty when starting search/discovery +// but also the device should be removed from the bluetooth cache +// this means iterate over devicedlg_ext->newdevList and call +// adapter->RemoveDevice(object_path, error) on the device + disconnect(app->manager, SIGNAL(deviceRemoved(const TQString&)), + this, TQT_SLOT(slotDeviceRemoved(const TQString&))); + TQListViewItemIterator it2(devicedlg_ext->newdevList); + while (it2.current()) + { + TQString addr = it2.current()->text(1); + TDEBluetoothApp::DevicesMap::Iterator dit = app->devices.begin(); + for (dit; dit != app->devices.end(); ++dit) + { + TQT_DBusError dbuserr; + TQString thepath = dit.data()->getPath(); + TQString theaddr = dit.data()->getAddress(dbuserr); + if (dbuserr.isValid()) + tqDebug("Device getAddress failed: %s", dbuserr.message().local8Bit().data()); + if (addr == theaddr) + { + adapter->RemoveDevice(thepath, dbuserr); + if (dbuserr.isValid()) + tqDebug("Remove Device failed: %s", dbuserr.message().local8Bit().data()); + break; + } + } + ++it2; + } + devicedlg_ext->newdevList->clear(); + connect(app->manager, SIGNAL(deviceRemoved(const TQString&)), + this, TQT_SLOT(slotDeviceRemoved(const TQString&))); + devicedlg_ext->statusbar->setText(i18n("Device Discovery started")); + chgLogoTimer->start(LOGOTIMEOUT); + } + else + { + chgLogoTimer->stop(); + pixmapLabel->setPixmap(pix); + } +} + +void DeviceWizard::slotInsertDevice(const TQString& path) +{ + TQT_DBusError dbuserr; + TQString addr = app->devices[path]->getAddress(dbuserr); + if (dbuserr.isValid()) + tqDebug("Device getAddress failed: %s", dbuserr.message().local8Bit().data()); + TQString name = app->devices[path]->getName(dbuserr); + if (dbuserr.isValid()) + tqDebug("Device getName failed: %s", dbuserr.message().local8Bit().data()); + bool paired = app->devices[path]->getPaired(dbuserr); + if (dbuserr.isValid()) + tqDebug("Device getPaired failed: %s", dbuserr.message().local8Bit().data()); + TQ_UINT32 devclass = app->devices[path]->getClass(dbuserr); + if (dbuserr.isValid()) + tqDebug("Device getClass failed: %s", dbuserr.message().local8Bit().data()); + bool connected = app->devices[path]->getConnected(dbuserr); + if (dbuserr.isValid()) + tqDebug("Device getConnected failed: %s", dbuserr.message().local8Bit().data()); + + + connect(app->devices[path], SIGNAL(AsyncErrorResponseDetected(int /*asyncCallId*/, const TQT_DBusError)), + this, TQT_SLOT(slotAsyncErrorResponseDetected(int /*asyncCallId*/, const TQT_DBusError))); +// connect(app->devices[path], SIGNAL(ConnectAsyncReply(int /*asyncCallId*/)), +// this, TQT_SLOT(slotConnectAsyncReply(int /*asyncCallId*/))); +// connect(app->devices[path], SIGNAL(DisonnectAsyncReply(int /*asyncCallId*/)), +// this, TQT_SLOT(slotDisconnectAsyncReply(int /*asyncCallId*/))); +// connect(app->devices[path], SIGNAL(ConnectProfileAsyncReply(int /*asyncCallId*/)), +// this, TQT_SLOT(slotConnectProfileAsyncReply(int /*asyncCallId*/))); + + + TQListViewItem *devItem = deviceBox->findItem(addr, 2, TQt::ExactMatch); + //device was already setup but is not in the deviceBox + if (paired && !devItem) + { + TQListViewItem *toAddDev = new TQListViewItem(deviceBox); + toAddDev->setText(1, name); + toAddDev->setText(2, addr); + TQString iconName = DeviceMimeConverter::classToIconName(devclass); + KPixmap pix2 = TDEGlobal::iconLoader()->loadIcon(iconName, TDEIcon::Small, 16); + toAddDev->setPixmap(3, pix2); + + KPixmap pix3 = TDEGlobal::iconLoader()->loadIcon("bookmark", TDEIcon::Small, 16); + + if (connected) + toAddDev->setPixmap(0, pix3); + deviceBox->insertItem(toAddDev); + return; + } + + //device was not setup it belongs to the new device list + TQString mimeType = DeviceMimeConverter::classToMimeType(devclass); + TQString type; + if (mimeType == "bluetooth/peripheral-device-class") + { + type = i18n("peripheral"); + } + else if (mimeType == "bluetooth/av-device-class") + { + type = i18n("A/V"); + } + else if (mimeType == "bluetooth/phone-device-class") + { + type = i18n("phone"); + } + else if (mimeType == "bluetooth/wearable-device-class") + { + type = i18n("wearable"); + } + else if (mimeType == "bluetooth/toy-device-class") + { + type = i18n("toy"); + } + else if (mimeType == "bluetooth/health-device-class") + { + type = i18n("toy"); + } + else if (mimeType == "bluetooth/computer-device-class") + { + return; + } + else + { + type = i18n("unknown"); + } + + TQListViewItem *ndevlist = new TQListViewItem(devicedlg_ext->newdevList, name, addr); + + TQString iconName = DeviceMimeConverter::classToIconName(devclass); + KPixmap pix2 = TDEGlobal::iconLoader()->loadIcon(iconName, TDEIcon::Small, 32); + ndevlist->setPixmap(2, pix2); + + devicedlg_ext->newdevList->insertItem(ndevlist); + + int size1 = devicedlg_ext->newdevList->columnWidth(0); + devicedlg_ext->newdevList->setColumnWidth(0, size1 + 15); + + int size2 = devicedlg_ext->newdevList->columnWidth(1); + devicedlg_ext->newdevList->setColumnWidth(1, size2 + 15); + + devicedlg_ext->statusbar->setText(i18n("Found new %1 device.").arg(type)); +} + +void DeviceWizard::slotDeviceNameChanged(const TQString& path, const TQString& newname) +{ + TQT_DBusError dbuserr; + TQString addr = app->devices[path]->getAddress(dbuserr); + if (dbuserr.isValid()) + tqDebug("Device getAddress failed: %s", dbuserr.message().local8Bit().data()); + + TQListViewItem *tmp = devicedlg_ext->newdevList->findItem(addr, 1, TQt::ExactMatch); + if (tmp) { + if (tmp->text(0) == "") + { + const TQPixmap *ico = tmp->pixmap(2); + devicedlg_ext->newdevList->takeItem(tmp); + TQListViewItem *ndevlist = new TQListViewItem(devicedlg_ext->newdevList, newname, addr); + if (ndevlist) + { + ndevlist->setPixmap(2, (*ico)); + devicedlg_ext->newdevList->insertItem(ndevlist); + } + } + return; + } + + tmp = deviceBox->findItem(addr, 2, TQt::ExactMatch); + if (tmp) { + if (tmp->text(0) == "") + { + const TQPixmap *ico = tmp->pixmap(2); + deviceBox->takeItem(tmp); + TQListViewItem *devlist = new TQListViewItem(deviceBox, newname, addr); + if (devlist) + { + devlist->setPixmap(2, (*ico)); + deviceBox->insertItem(devlist); + } + } + } +} + +void DeviceWizard::slotAsyncErrorResponseDetected(int asyncCallId, const TQT_DBusError dbuserr) +{ + tqDebug("AsyncErrorResponseDetected (%i): %i %s %s", asyncCallId, dbuserr.type(), dbuserr.name().local8Bit().data(), dbuserr.message().local8Bit().data()); + + connectingDevice = {ConState::IDLE, TQString(), TQStringList()}; + connectButton->setText(i18n("C&onnect")); + setStateDeviceButtons(true); + + KNotifyClient::event(TDEApplication::kApplication()->mainWidget()->winId(), + "ConnectionError", tr("AsyncErrorResponseDetected: %1\n%2\n%3") + .arg(dbuserr.type()) + .arg(dbuserr.name().local8Bit().data()) + .arg(dbuserr.message().local8Bit().data())); +} + +void DeviceWizard::slotSetupNewDevice() +{ + TQListViewItem *confSel = devicedlg_ext->newdevList->currentItem(); + if (!confSel) + { + TQMessageBox::information(this, + i18n("Setup device"), + i18n("You have to select a remote Device to setup!"), + TQMessageBox::Ok | TQMessageBox::Default, + TQMessageBox::NoButton, + TQMessageBox::NoButton); + return; + } + + TQString selAddr = confSel->text(1); + DeviceImpl *newdev; + + TDEBluetoothApp::DevicesMap::Iterator dit = app->devices.begin(); + for (dit; dit != app->devices.end(); ++dit) + { + TQT_DBusError dbuserr; + TQString addr = dit.data()->getAddress(dbuserr); + if (dbuserr.isValid()) + tqDebug("Device getAddress failed: %s", dbuserr.message().local8Bit().data()); + if (selAddr == addr) + { + newdev = dit.data(); + break; + } + } + + if (!newdev) + { + TQMessageBox::information(this, + i18n("Setup device"), + i18n("No device matching selection was found!"), + TQMessageBox::Ok | TQMessageBox::Default, + TQMessageBox::NoButton, + TQMessageBox::NoButton); + return; + } + + setStateDeviceButtons(false); + devicesetupwizard->setDevice(newdev); + devicesetupwizard->show(); + devicesetupwizard->raise(); + devicedlg_ext->setupButton->setEnabled(false); + + kdDebug() << "address: _____________" << selAddr << endl; +} + +void DeviceWizard::slotDevicePairedChanged(const TQString& path, bool) +{ + TQListViewItem *confSel = devicedlg_ext->newdevList->currentItem(); + if (!confSel) + return; + + TQString selAddr = confSel->text(1); + TQListViewItem* newDeviceBoxItem = new TQListViewItem(deviceBox); + newDeviceBoxItem->setText(1, confSel->text(0)); + newDeviceBoxItem->setText(2, selAddr); + + TDEBluetoothApp::DevicesMap::Iterator dit = app->devices.begin(); + for (dit; dit != app->devices.end(); ++dit) + { + TQT_DBusError dbuserr; + TQString addr = dit.data()->getAddress(dbuserr); + if (dbuserr.isValid()) + tqDebug("Device getAddress failed: %s", dbuserr.message().local8Bit().data()); + if (addr == selAddr) + { + TQString iconName = DeviceMimeConverter::classToIconName(dit.data()->getClass(dbuserr)); + KPixmap pix4 = TDEGlobal::iconLoader()->loadIcon(iconName, TDEIcon::Small, 16); + newDeviceBoxItem->setPixmap(3, pix4); + break; + } + } + + deviceBox->insertItem(newDeviceBoxItem); + delete confSel; +} + +//void DeviceWizard::slotMediaControlConnectedChanged(const TQString &path, +// bool connect) +//{ +// if (connect) +// mediaCtlDialog = new MediaControl(path, app->manager->getConnection()); +// else +// mediaCtlDialog->close(); +//} + +void DeviceWizard::slotUpdateLogo() +{ + if (!addButton->isOn()) { + timer = false; + pixmapLabel->setPixmap(pix); + return; + } + + if (timer == false) + { + pixmapLabel->setPixmap(logoBlend); + timer = true; + } + else + { + pixmapLabel->setPixmap(pix); + timer = false; + } +} + +#include "devicewizard.moc" + diff --git a/src/tdebluez/devicewizard.h b/src/tdebluez/devicewizard.h new file mode 100644 index 0000000..dae8fe5 --- /dev/null +++ b/src/tdebluez/devicewizard.h @@ -0,0 +1,123 @@ +/* + * + * Device Manager Gui for tdebluez + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of tdebluez. + * + * tdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * tdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#ifndef DEVICEWIZARD_H +#define DEVICEWIZARD_H + +#include +#include +#include +// +#include +#include +#include +#include + +#include +#include +#include + +#include "devicedialog.h" +#include "devicedialog_ext.h" +#include "devicesetupwizard.h" +//#include "mediacontrol.h" + +using namespace TDEBluetooth; + +class DeviceWizard: public DeviceDialog +{ + Q_OBJECT + +public: + DeviceWizard(TDEBluetoothApp* app); + ~DeviceWizard(); + +private: + void setStateDeviceButtons(bool state); + +private slots: + // device wizard ui + void slotDeviceBoxClicked(TQListViewItem*); + void slotChangeSetupButton(TQListViewItem*); + void slotConnectButton(); + void slotCloseDialog(); + void slotSearch(bool state); + void slotUpdateLogo(); + void slotInsertDevice(const TQString& path); + void slotSetupNewDevice(); + void slotDeleteDevice(); + void slotConfigDevice(); + + // supportive + void slotConnectNextProfile(); + void slotSetAdapter(const TQString& path, const TQString& name); + + // MANAGER -> device wizard + // adapter related + void slotAdapterDiscoveringChanged(const TQString&, bool); + // void slotMediaControlConnectedChanged(const TQString&, bool); + // device related +// void slotSetConnectedIcon(const TQString&); +// void slotSetDisconnectedIcon(const TQString&); + void slotDeviceConnectedChanged(const TQString&, bool); + void slotDeviceRemoved(const TQString& path); + void slotDevicePairedChanged(const TQString&, bool); + void slotDeviceNameChanged(const TQString&, const TQString&); + + void slotAsyncErrorResponseDetected(int asyncCallId, const TQT_DBusError dbuserr); +// void slotConnectAsyncReply(int asyncCallId); +// void slotConnectProfileAsyncReply(int asyncCallId); + + + +private: + TDEBluetoothApp *app; + AdapterImpl *adapter; + + DeviceDialog_Ext *devicedlg_ext; + DeviceSetupWizard *devicesetupwizard; + // MediaControl *mediaCtlDialog; + + enum ConState + { + IDLE = 0, CONNECTING, DISCONNECTING + }; + + struct ConnectDevice { + ConState state; + TQString address; + TQStringList profiles; // when multiple profiles are to be connected + }; + + KPixmap logoBlend; + KPixmap pix; + KPixmap mainlogo; + + TQTimer* chgLogoTimer; + bool timer; + + ConnectDevice connectingDevice; +}; + +#endif diff --git a/src/tdebluez/eventsrc b/src/tdebluez/eventsrc new file mode 100644 index 0000000..c4fb1a2 --- /dev/null +++ b/src/tdebluez/eventsrc @@ -0,0 +1,397 @@ +[!Global!] +IconName=tdebluez +Comment=TDEBluetooth +Comment[sv]=TDEBluetooth +Comment[tr]=TDEBluetooth + +[AdapterAttached] +Name=AdapterAttached +Name[bg]=Прикачен адаптер +Comment=Adapter attached +Comment[bg]=Адаптер за блутут беше прикачен към системата +default_presentation=20 + +[AdapterDetached] +Name=AdapterDetached +Name[bg]=Отстранен адаптер +Comment=Adapter detached +Comment[bg]=Адаптер за блутут беше отстранен от системата +default_presentation=20 + +[AdapterAvailable] +Name=AdapterAvailable +Name[bg]=Адаптер на разположение +Comment=Adapter available +Comment[bg]=Адаптер за блутут е на разположение +default_presentation=20 + +[IncomingConnection] +Name=IncomingConnection +Name[bg]=Входяща връзка +Name[br]=Kevreadenn resev +Name[cy]=CysylltiadCyrraedd +Name[da]=Indgående forbindelse +Name[de]=EingehendeVerbindung +Name[el]=Εισερχόμενη σύνδεση +Name[es]=Conexión entrante +Name[et]=Sisenev ühendus +Name[fr]=Connexion entrante +Name[ga]=CeangalIsteach +Name[gl]=Chamada Entrante +Name[it]=ConnessioneInArrivo +Name[lt]=Įeinantis ryšys +Name[nl]=Inkomende verbinding +Name[pl]=Połączenie przychodzące +Name[pt]=Ligação Recebida +Name[pt_BR]=Ligação Recebida +Name[sr]=Долазна веза +Name[sr@Latn]=Долазна веза +Name[sv]=Inkommande anslutning +Name[ta]=உள் வரும் இனைப்பு +Name[tg]=Пайвастшавии даромад +Name[tr]=Gelen Bağlandı +Comment=Received incoming connection +Comment[bg]=Получаване на входяща връзка +Comment[bs]=Primljena dolazeća konekcija +Comment[ca]=S'ha rebut una connexió entrant +Comment[cs]=Obdrženo příchozí spojení +Comment[cy]=Derbynwyd cysylltiad a gyrhaeddodd +Comment[da]=Modtog indkommende forbindelse +Comment[de]=Empfangene eingehende Verbindung +Comment[el]=Λήψη εισερχόμενης σύνδεσης +Comment[es]=Se ha recibido una conexión entrante +Comment[et]=Saadi sisenev ühendus +Comment[fr]=Connexion rentrante reçue +Comment[gl]=Foi recebida unha chamada +Comment[it]=Connessione in arrivo ricevuta +Comment[ja]=着信接続がありました +Comment[lt]=Priimtas įeinantis ryšys +Comment[nl]=Ontving een inkomende verbinding +Comment[pl]=Otrzymano połączenie przychodzące +Comment[pt]=Foi recebida uma ligação +Comment[pt_BR]=Foi recebida uma ligação +Comment[sr]=Примљена је долазећа веза +Comment[sr@Latn]=Примљена је долазећа веза +Comment[sv]=Tog emot inkommande anslutning +Comment[ta]=உள் வரும் இனைப்பை பேற்றுள்ளது +Comment[tg]=Пайвастшавии даромад қабул шуд +default_presentation=20 + +[ProcessStderr] +Name=ProcessStderr +Name[de]=ProzessStderr +Name[el]=Διεργασία Stderr +Name[es]=Error estándar de proceso +Name[fr]=Stdett du processus +Name[gl]=ProcesStderr +Name[it]=StderrDelProcesso +Name[pl]=Wyjście diagnostyczne procesu +Name[pt]=Processamento do STDERR +Name[pt_BR]=Processamento do STDERR +Name[sr]=Stderr процеса +Name[sr@Latn]=Stderr процеса +Name[sv]=Processens standardfelutmatning +Comment=Output of search job on stderr +Comment[bg]=Изходни данни за търсене на работа на stderr +Comment[ca]=Resultat del treball de cerca en stderr +Comment[da]=Uddata fra søgejob til stderr +Comment[de]=Ausgabe des Suchauftrags auf stderr +Comment[el]=Έξοδος της εργασίας αναζήτησης στο stderr +Comment[es]=Salida del trabajo de búsqueda de error +Comment[et]=Otsingutöö väljund stderri +Comment[fr]=Sortie de la tâche de recherche sur stderr +Comment[gl]=Resultado do traballo de procura en stderr +Comment[it]=Output del processo di ricerca su stderr +Comment[ja]=検索ジョブの stderr への出力 +Comment[lt]=Paieškos rezultato išvedimas į stderr +Comment[nl]=Uitvoer van zoektaak op stderr +Comment[pl]=Wyjście zadania wyszukiwania na standardowe wyjście diagnostyczne +Comment[pt]=Resultado da tarefa de pestquisa no 'stderr' +Comment[pt_BR]=Resultado da tarefa de pestquisa no 'stderr' +Comment[sr]=Излаз посла тражења на stderr +Comment[sr@Latn]=Излаз посла тражења на stderr +Comment[sv]=Utmatning från sökjobb på standardfelutmatningen +Comment[ta]=stderr இல் தேடும் பணியின் வெளியீடு +Comment[tg]=Натиҷаи ҷустуҷӯи корҳои stderr +default_presentation=28 +default_sound=KDE_Critical_Error.wav + +[ProcessStdout] +Name=ProcessStdout +Name[de]=ProzessStdout +Name[el]=Διεργασία Stdout +Name[es]=Salida estándar de proceso +Name[fr]=Stdout du processus +Name[gl]=ProcesStdout +Name[it]=StdoutProcesso +Name[pl]=Wyjście standardowe procesu +Name[pt]=Processamento do STDOUT +Name[pt_BR]=Processamento do STDOUT +Name[sr]=Stdout процеса +Name[sr@Latn]=Stdout процеса +Name[sv]=Processens standardutmatning +Comment=Output of search job on stdout +Comment[bg]=Изходни данни за търсене на работа на stdout +Comment[ca]=Resultat del treball de cerca en stdout +Comment[da]=Uddata fra søgejob til stdout +Comment[de]=Ausgabe des Suchauftrags auf stdout +Comment[el]=Έξοδος της εργασίας αναζήτησης στο stdout +Comment[es]=Salida del trabajo de búsqueda en la salida estándar +Comment[et]=Otsingutöö väljund stdouti +Comment[fr]=Sortie de la tâche de recherche sur stdout +Comment[gl]=Resultado do traballo de procura en stdout +Comment[it]=Output del processo di ricerca su stdout +Comment[ja]=検索ジョブの stdout への出力 +Comment[lt]=Paieškos rezultato išvedimas į stdout +Comment[nl]=Uitvoer van zoektaak op stdout +Comment[pl]=Wyjście zadania wyszukiwania na standardowe wyjście procesu +Comment[pt]=Resultado da tarefa de pestquisa no 'stdout' +Comment[pt_BR]=Resultado da tarefa de pestquisa no 'stdout' +Comment[sr]=Излаз посла тражења на stdout +Comment[sr@Latn]=Излаз посла тражења на stdout +Comment[sv]=Utmatning från sökjobb på standardutmatningen +Comment[ta]=stdout இல் தேடும் பணியின் வெளியீடு +default_presentation=8 + +[ProcessFailed] +Name=ProcessFailed +Name[bg]=Грешка при изпълнение +Name[cs]=Proces selhal +Name[cy]=MethoddProses +Name[de]=ProzessFehlgeschlagen +Name[el]=Η διεργασία απέτυχε +Name[es]=Proceso fallido +Name[et]=Protsess nurjus +Name[fr]=Échec du processus +Name[ga]=PróiseasTeipthe +Name[gl]=ProcesFailed +Name[it]=ProcessoNonRiuscito +Name[lt]=Procesas baigėsi klaida +Name[nl]=Proces mislukt +Name[pa]=ਕਾਰਜ ਫੇਲ +Name[pl]=Błąd procesu +Name[pt]=Processo Mal-Sucedido +Name[pt_BR]=Processo Mal-Sucedido +Name[sr]=Неуспео процес +Name[sr@Latn]=Неуспео процес +Name[sv]=Processen misslyckades +Name[ta]=சேயல் இயந்தது +Name[tg]=Амалиёт қатъ шуд +Name[tr]=Süreç Hatası +Comment=Could not call process to handle connection +Comment[bg]=Грешка при изпълнение на процес за обработка на връзка +Comment[bs]=Nisam mogao pozvati proces koji rukuje konekcijom +Comment[ca]=No s'ha pogut cridar al procés per a manejar la connexió +Comment[cs]=Nelze spustit proces k obsluze spojení +Comment[cy]=Methu galw proses i drin y cysylltiad +Comment[da]=Kunne ikke kalde proces til at håndtere forbindelse +Comment[de]=Prozess zum Behandeln der Verbindung konnte nicht aufgerufen werden +Comment[el]=Αδυναμία κλήσης της διεργασίας για τον έλεγχο της σύνδεσης +Comment[es]=No se pudo llamar al proceso que maneja la conexión +Comment[et]=Ei suuda käivitada protsessi ühenduse käsitlemiseks +Comment[fr]=Impossible d'appeler le processus pour ouvrir la connexion +Comment[gl]=Non foi posíbel chamar o proceso de xestión da conexón +Comment[it]=Impossibile chiamare processo per gestire la connessione +Comment[ja]=接続を扱うプロセスを呼び出せませんでした +Comment[lt]=Nepavyko iškviesti proceso prisijungimui apdoroti +Comment[nl]=Het proces om de verbinding af te handelen kon niet worden aangeroepen +Comment[pl]=Nie można wywołać procesu do obsługi połączenia +Comment[pt]=Não foi possível invocar o processo para tratar da ligação +Comment[pt_BR]=Não foi possível invocar o processo para tratar da ligação +Comment[sr]=Нисам могао да позовем процес за руковање везом +Comment[sr@Latn]=Нисам могао да позовем процес за руковање везом +Comment[sv]=Kunde inte anropa process för att hantera anslutningen +Comment[ta]=இனைப்பை கையாள செயல் எதுவும் இல்லை +Comment[tg]=Амалиёт барои пайвастшавӣ фаъол карда натавонист +default_presentation=15 +default_sound=KDE_Critical_Error.wav + +[BluetoothDeviceLost] +Name=BluetoothDeviceLost +Name[bg]=Връзката с Bluetooth устройството е изгубена +Name[de]=BluetoothGerätVerloren +Name[el]=Η συσκευή Bluetooth χάθηκε +Name[es]=Dispositivos bluetooth perdidos +Name[et]=Bluetooth seade kadunud +Name[fr]=Périphérique Bluetooth Perdu +Name[it]=DispositivoBluetoothPerso +Name[lt]=Ryšys su bluetooth įrenginiu nutrūko +Name[nl]=Bluetooth-apparaat verloren +Name[pl]=Utracono urządzenie +Name[pt]=Dispositivo Bluetooth Perdido +Name[pt_BR]=Dispositivo Bluetooth Perdido +Name[sr]=Bluetooth уређај је изгубљен +Name[sr@Latn]=Bluetooth уређај је изгубљен +Name[sv]=Blåtandsenhet förlorad +Name[ta]=புலுடுத் கருவி தொலைந்தது +Name[tg]=Дастгоҳи Bluetooth гум шуд +Name[tr]=Bluetooth Aygıtı Kaybedildi +Comment=The Bluetooth device failed or was unplugged +Comment[bg]=Bluetooth устройство се повреди или е било изключено +Comment[bs]=Bluetooth uređaj je izazvao grešku ili je iskopčan +Comment[ca]=El dispositiu Bluetooth ha fallat o ha estat desendollat +Comment[da]=Bluetooth-enheden mislykkedes eller var ikke sat til +Comment[de]=Das Bluetooth-Gerät meldet einen Fehler oder wurde entfernt +Comment[el]=Η συσκευή Bluetooth απέτυχε ή δεν είναι συνδεδεμένη +Comment[es]=Falló el dispositivo bluetooth o se desconectó +Comment[et]=Bluetooth seade tõrkus või ei ole ühendatud +Comment[fr]=Le périphérique Bluetooth a planté ou a été débranché +Comment[gl]=O dispositivo Bluetooth fallou ou foi desconectado +Comment[it]=Il dispositivo Bluetooth è in errore o è stato scollegato +Comment[ja]=Bluetooth デバイスに問題があるか、取り外されました +Comment[lt]=Bluetooth įrenginys nesuveikė arba buvo atjungtas +Comment[nl]=Het Bluetooth-apparaat faalde of is niet aangesloten +Comment[pl]=Urządzenie Bluetooth zostało odłączone lub wystąpił w nim błąd +Comment[pt]=O dispositivo Bluetooth falhou ou foi desligado +Comment[pt_BR]=O dispositivo Bluetooth falhou ou foi desligado +Comment[sr]=Bluetooth уређај није у реду или је искључен +Comment[sr@Latn]=Bluetooth уређај није у реду или је искључен +Comment[sv]=Blåtandsenheten slutade fungera eller kopplades bort +Comment[ta]=புலுடுத் கருவி சேயல் இயந்தது அல்லது இனைப்பு துண்டிக்க பட்டது +Comment[tg]=Дастгоҳи Bluetooth қатъ шуд ё ҷудо карда шуд +Comment[tr]=Bluetooth aygıtı başarısız oldu ya da çıkartıldı +default_presentation=21 +default_sound=KDE_Dialog_Disappear.wav + +[BluetoothDeviceFound] +Name=BluetoothDeviceFound +Name[bg]=Открито е устройство Bluetooth +Name[de]=BluetoothGerätGefunden +Name[el]=Βρέθηκε συσκευή Bluetooth +Name[es]=Dispositivo bluetooth encontrado +Name[et]=Bluetooth seade leitud +Name[fr]=Périphérique Bluetooth Trouvé +Name[it]=DispositivoBluetoothTrovato +Name[lt]=Aptiktas bluetooth įrenginys +Name[nl]=Bluetooth-apparaat gevonden +Name[pl]=Znaleziono urządzenie Bluetooth +Name[pt]=Dispositivo Bluetooth Encontrado +Name[pt_BR]=Dispositivo Bluetooth Encontrado +Name[sr]=Нађен Bluetooth уређај +Name[sr@Latn]=Нађен Bluetooth уређај +Name[sv]=Blåtandsenhet hittades +Name[ta]=புலுடுத் கருவி கிடைத்தது +Name[tg]=Дастгоҳи Bluetooth ёфт нашуд +Name[tr]=Bluetooth Aygıtı Bulundu +Comment=The Bluetooth device was detected +Comment[ar]=تم اكتشلف جهاز Bluetooth +Comment[bg]=Открито е устройство Bluetooth +Comment[bs]=Bluetooth uređaj je detektovan +Comment[ca]=El dispositiu Bluetooth ha estat detectat +Comment[da]=Bluetooth-enheden blev detekteret +Comment[de]=Das Bluetooth-Gerät wurde erkannt +Comment[el]=Η συσκευή Bluetooth εντοπίστηκε +Comment[es]=Se detectó el dispositivo bluetooth +Comment[et]=Tuvastati Bluetooth seade +Comment[fr]=Le périphérique Bluetooth a été détecté +Comment[gl]=O dispositivo Bluetooth foi detectado +Comment[it]=Il dispositivo Bluetooth è stato rilevato +Comment[ja]=Bluetooth デバイスを検出しました +Comment[lt]=Aptiktas naujas bluetooth įrenginys +Comment[nl]=Het Bluetooth-apparaat is gevonden +Comment[pa]=ਬਲਿਊਟੁੱਥ ਜੰਤਰ ਨਹੀਂ ਮਿਲਿਆ ਹੈ +Comment[pl]=Znaleziono urządzenie Bluetooth +Comment[pt]=Foi detectado um dispositivo Bluetooth +Comment[pt_BR]=Foi detectado um dispositivo Bluetooth +Comment[sr]=Bluetooth уређај је пронађен +Comment[sr@Latn]=Bluetooth уређај је пронађен +Comment[sv]=Blåtandsenheten detekterades +Comment[ta]=புலுடுத் கருவி கண்டறிய பட்டது +Comment[tg]=Дастгоҳи Bluetooth пайдо шуд +Comment[tr]=Bluetooth aygıtı bulundu +default_presentation=21 +default_sound=KDE_Dialog_Appear.wav + +[NoChannelFound] +Name=NoChannelFound +Name[bg]=Не е открит канал +Name[de]=KeinKanalGefunden +Name[el]=Δε βρέθηκε κανάλι +Name[es]=No se encontró el canal +Name[et]=Kanaleid pole +Name[fr]=Aucun Canal trouvé +Name[it]=NessunCanaleTrovato +Name[lt]=Neaptiktas joks kanalas +Name[nl]=Geen kanaal gevonden +Name[pl]=Kanał nie odnaleziony +Name[pt]=Nenhum Canal Encontrado +Name[pt_BR]=Nenhum Canal Encontrado +Name[sr]=Није нађен канал +Name[sr@Latn]=Није нађен канал +Name[sv]=Ingen kanal hittades +Name[ta]=அலைவரிசை எதுவும் கிடைக்கவில்லை +Name[tg]=Ягон канал ёфт нашуд +Comment=No channel could be assigned to a service +Comment[ar]=لم اتمكن من تعيين قناة للخدمة +Comment[bg]=Не е открит канал за услуга +Comment[bs]=Nisam mogao pridružiti kanal usluzi +Comment[ca]=No s'ha pogut assignar cap canal a un servei +Comment[da]=Ingen kanal kunne tilknyttes en tjeneste +Comment[de]=Es konnte dem Dienst kein Kanal zugeordnet werden +Comment[el]=Δε βρέθηκε κανάλι για την αντιστοίχηση με μια υπηρεσία +Comment[es]=No se pudo asignar ningún canal al servicio +Comment[et]=Teenusele pole võimalik kanalit eraldada +Comment[fr]=Aucun canal n'a pu être assigné au service +Comment[gl]=Non foi posíbel atribuir un canal ao servizo +Comment[it]=Nessun canale ha potuto essere assegnato a un servizio +Comment[ja]=サービスにチャネルを割り当てられませんでした +Comment[lt]=Tarnybai nepavyko priskirti jokio kanalo +Comment[nl]=Er kon geen kanaal worden toegewezen aan een dienst +Comment[pl]=Żaden kanał nie mógł zostać przypisany do urządzenia +Comment[pt]=Não foi possível atribuir um canal a um serviço +Comment[pt_BR]=Não foi possível atribuir um canal a um serviço +Comment[sr]=Нема канала који се може доделити услузи +Comment[sr@Latn]=Нема канала који се може доделити услузи +Comment[sv]=Någon kanal kunde inte tilldelas till en tjänst +Comment[ta]=சேவைக்கு எந்த ஒரு அலைவரிசையும் கோடுக்கபட வில்லை +default_presentation=15 +default_sound=KDE_Critical_Error.wav + +[ConnectionError] +Name=ConnectionError +Name[ar]=خطأ عند الاتصال +Name[bg]=Грешка при връзка +Name[br]=Fazi Kevreadur +Name[de]=Verbindungsfehler +Name[el]=Σφάλμα σύνδεσης +Name[es]=Error de conexión +Name[et]=Ühenduse viga +Name[fr]=Erreur de Connexion +Name[it]=ErroreDiConnessione +Name[lt]=Prisijungimo klaida +Name[nl]=Verbindingsfout +Name[pl]=Błąd połączenia +Name[pt]=Erro de Ligação +Name[pt_BR]=Erro de Ligação +Name[sr]=Грешка везе +Name[sr@Latn]=Грешка везе +Name[sv]=Anslutningsfel +Name[ta]=இனைப்பு பிழை +Name[tg]=Хатои пайвастшавӣ +Comment=Error while setting up a connection +Comment[ar]=خطأ عند أعداد الاتصال +Comment[bg]=Грешка при установяване на връзка +Comment[bs]=Greška prilikom podešavanja konekcije +Comment[ca]=Error mentre s'establia una connexió +Comment[da]=Fejl ved opsætning af en forbindelse +Comment[de]=Fehler beim Aufbauen einer Verbindung +Comment[el]=Σφάλμα κατά την εγκαθίδρυση μιας σύνδεσης +Comment[es]=Error durante la configuración de una conexión +Comment[et]=Viga ühenduse loomisel +Comment[fr]=Erreur lors de la mise en place de la connexion +Comment[gl]=Erro ao configurar unha conexón +Comment[it]=Errore durante l'impostazione di una connessione +Comment[ja]=接続のセットアップ中にエラーが発生しました +Comment[lt]=Klaida užmezgant ryšį +Comment[nl]=Fout bij het opzetten van een verbinding +Comment[pa]=ਕੁਨੈਕਸ਼ਨ ਬਣਾਉਣ ਦੌਰਾਨ ਗਲਤੀ ਆਈ ਹੈ +Comment[pl]=Błąd w trakcie przygotowywania połączenia +Comment[pt]=Ocorreu um erro ao configurar uma ligação +Comment[pt_BR]=Ocorreu um erro ao configurar uma ligação +Comment[sr]=Грешка при подешавању везе +Comment[sr@Latn]=Грешка при подешавању везе +Comment[sv]=Fel när en anslutning skulle upprättas +Comment[ta]=இனைப்பை அமைக்கையில் பிழை +Comment[tg]=Ҳангоми танзими пайвастшавӣ хато пайдо шуд +default_presentation=21 +default_sound=KDE_Error.wav diff --git a/src/tdebluez/main.cpp b/src/tdebluez/main.cpp new file mode 100644 index 0000000..6ef0336 --- /dev/null +++ b/src/tdebluez/main.cpp @@ -0,0 +1,83 @@ +/* + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of tdebluez. + * + * tdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * tdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include + +#include "application.h" +static const char *description = I18N_NOOP( + "TDEBluetooth is a bluetooth manager\nfor the Trinity Desktop."); + +static TDECmdLineOptions options[] = +{ + { + "dontforceshow", + I18N_NOOP("Show the tray icon if it was disabled before."), + 0 + }, + { 0, 0, 0 } +}; + +extern "C" KDE_EXPORT int kdemain(int argc, char **argv) +{ + TDELocale::setMainCatalogue("tdebluez"); + TDEAboutData aboutData( "tdebluez", + I18N_NOOP("TDEBluetooth"), + 0, + description, + TDEAboutData::License_GPL, + "(c) 2018-, Emanoil Kotsev\n(c) 2003-2004, Fred Schaettgen", + 0, + "http://trinitydesktop.org"); + aboutData.addAuthor( "Emanoil Kotsev", + I18N_NOOP("Port to TDE and fundamental rework with Bluez5"), + "deloptes@gmail.com"); + TDECmdLineArgs::init(argc, argv, &aboutData); + TDECmdLineArgs::addCmdLineOptions(options); + KUniqueApplication::addCmdLineOptions(); + + if (!KUniqueApplication::start()) + { + std::cerr << i18n("TDEBluetooth is already running.\n").local8Bit(); + return 0; + } + + TDEBluetoothApp a; + + if (!a.isConnected()) + { + KMessageBox::error(NULL, i18n("Can't connect to DBus!\nUnable to start tdebluez. \n\n \ + Restart dbus and the bluetooth service")); + // debug message for testing + std::cerr << i18n("Can't connect to DBus!\n").local8Bit(); + KUniqueApplication::kApplication()->quit(); + return 0; + } + else + { + return a.exec(); + } + +} + diff --git a/src/tdebluez/mediacontrol.cpp b/src/tdebluez/mediacontrol.cpp new file mode 100644 index 0000000..6d9afac --- /dev/null +++ b/src/tdebluez/mediacontrol.cpp @@ -0,0 +1,181 @@ +/* + * + * MediaControl for tdebluez + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of tdebluez. + * + * tdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * tdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +#include +#include +#include +#include "mediacontrol.h" + +MediaControl::MediaControl(TQString path, TQT_DBusConnection *conn) : + MediaCtlDlg() +{ + pixStart = TDEGlobal::iconLoader()->loadIcon("media-playback-start", TDEIcon::Small, 22); + pixPause = TDEGlobal::iconLoader()->loadIcon("media-playback-pause", TDEIcon::Small, 22); + + tQPushButtonPlay->setPixmap(pixStart); + tQPushButtonSeekForward->setPixmap(TDEGlobal::iconLoader()->loadIcon("media-seek-forward", TDEIcon::Small, 22)); + tQPushButtonSeekBackward->setPixmap(TDEGlobal::iconLoader()->loadIcon("media-seek-backward", TDEIcon::Small, 22)); + tQPushButtonForward->setPixmap(TDEGlobal::iconLoader()->loadIcon("media-skip-forward", TDEIcon::Small, 22)); + tQPushButtonBackward->setPixmap(TDEGlobal::iconLoader()->loadIcon("media-skip-backward", TDEIcon::Small, 22)); + tQPushButtonStop->setPixmap(TDEGlobal::iconLoader()->loadIcon("media-playback-stop", TDEIcon::Small, 22)); + + tQVolumeSlider->setTracking(false); + tQVolumeSlider->setRange(0, 100); + volume = 50; + tQVolumeSlider->setValue(volume); + + mPath = path; + mediaCtlProxy = new org::bluez::MediaControl1Proxy("org.bluez", path); + mediaCtlProxy->setConnection((*(conn))); + + connect((TQObject*) tQPushButtonPlay, SIGNAL(clicked()), this, TQT_SLOT(slotPlay())); + connect((TQObject*) tQPushButtonPlay, SIGNAL(toggled(bool)), this, TQT_SLOT(slotPlayToggled(bool))); + connect((TQObject*) tQPushButtonSeekForward, SIGNAL(clicked()), this, TQT_SLOT(slotFastForward())); + connect((TQObject*) tQPushButtonSeekBackward, SIGNAL(clicked()), this, TQT_SLOT(slotRewind())); + connect((TQObject*) tQPushButtonForward, SIGNAL(clicked()), this, TQT_SLOT(slotNext())); + connect((TQObject*) tQPushButtonBackward, SIGNAL(clicked()), this, TQT_SLOT(slotPrevious())); + connect((TQObject*) tQPushButtonStop, SIGNAL(clicked()), this, TQT_SLOT(slotStop())); + // tQProgressSlider + connect((TQObject*) tQVolumeSlider, SIGNAL(valueChanged(int)), this, TQT_SLOT(slotVolumeValueChanged(int))); + // tQPushButtonRepeat + show(); +} + +MediaControl::~MediaControl() +{ + if (mediaCtlProxy) + delete mediaCtlProxy; +} + +//void MediaControl::closeMediaControl() +//{ +// +//} + +void MediaControl::slotPlay() +{ + kdDebug() << k_funcinfo << endl; + TQT_DBusError error; + mediaCtlProxy->Play(error); + if (error.isValid()) + tqDebug("Media Play failed: %s", error.message().local8Bit().data()); +} + +void MediaControl::slotPlayToggled(bool state) +{ + kdDebug() << k_funcinfo << endl; + if (state) + { + tQPushButtonPlay->setPixmap(pixPause); + } + else + { + slotPause(); + tQPushButtonPlay->setPixmap(pixStart); + } +} + +void MediaControl::slotStop() +{ + kdDebug() << k_funcinfo << endl; + TQT_DBusError error; + if (mediaCtlProxy->Stop(error)) + { + if (tQPushButtonPlay->isOn()) + tQPushButtonPlay->toggle(); + } + + if (error.isValid()) + tqDebug("Media Stop failed: %s", error.message().local8Bit().data()); +} + +void MediaControl::slotPause() +{ + kdDebug() << k_funcinfo << endl; + TQT_DBusError error; + mediaCtlProxy->Pause(error); + if (error.isValid()) + tqDebug("Media Pause failed: %s", error.message().local8Bit().data()); +} + +void MediaControl::slotNext() +{ + kdDebug() << k_funcinfo << endl; + TQT_DBusError error; + mediaCtlProxy->Next(error); + if (error.isValid()) + tqDebug("Media Next failed: %s", error.message().local8Bit().data()); +} + +void MediaControl::slotPrevious() +{ + kdDebug() << k_funcinfo << endl; + TQT_DBusError error; + mediaCtlProxy->Previous(error); + if (error.isValid()) + tqDebug("Media Previous failed: %s", error.message().local8Bit().data()); +} + +void MediaControl::slotFastForward() +{ + kdDebug() << k_funcinfo << endl; + TQT_DBusError error; + mediaCtlProxy->FastForward(error); + if (error.isValid()) + tqDebug("Media FastForward failed: %s", error.message().local8Bit().data()); +} + +void MediaControl::slotRewind() +{ + kdDebug() << k_funcinfo << endl; + TQT_DBusError error; + mediaCtlProxy->Rewind(error); + if (error.isValid()) + tqDebug("Media Rewind failed: %s", error.message().local8Bit().data()); +} + +void MediaControl::slotVolumeValueChanged(int val) +{ + kdDebug() << k_funcinfo << endl; + TQT_DBusError error; + if (val > volume) + { + TQT_DBusError error; + mediaCtlProxy->VolumeUp(error); + if (error.isValid()) + tqDebug("Media VolumeUp getPowered failed: %s", error.message().local8Bit().data()); + } + if (val < volume) + { + mediaCtlProxy->VolumeDown(error); + if (error.isValid()) + tqDebug("Media VolumeDown getPowered failed: %s", error.message().local8Bit().data()); + } + volume = val; +} + +#include "mediacontrol.moc" diff --git a/src/tdebluez/mediacontrol.h b/src/tdebluez/mediacontrol.h new file mode 100644 index 0000000..b096045 --- /dev/null +++ b/src/tdebluez/mediacontrol.h @@ -0,0 +1,66 @@ +/* + * + * MediaControl for tdebluez + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of tdebluez. + * + * tdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * tdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef MEDIACONTROL_H_INCLUDED +#define MEDIACONTROL_H_INCLUDED + +#include +#include + +#include +#include "mediactl.h" + +class MediaControl: public MediaCtlDlg +{ + Q_OBJECT + +public: + MediaControl(TQString path, TQT_DBusConnection* conn); + ~MediaControl(); +// void closeMediaControl(); + +public slots: + void slotPlay(); + void slotPlayToggled(bool); + void slotStop(); + void slotPause(); + void slotNext(); + void slotPrevious(); + void slotFastForward(); + void slotRewind(); + void slotVolumeValueChanged(int); + +private: + TQString mPath; + int volume; + org::bluez::MediaControl1Proxy *mediaCtlProxy; + TQPixmap pixStart; + TQPixmap pixPause; + +}; + +#endif //MEDIACONTROL_H_INCLUDED + +// End of File diff --git a/src/tdebluez/mediactl.ui b/src/tdebluez/mediactl.ui new file mode 100644 index 0000000..877969e --- /dev/null +++ b/src/tdebluez/mediactl.ui @@ -0,0 +1,265 @@ + +MediaCtlDlg + + + MediaCtlDlg + + + + 1 + 0 + 140 + 140 + + + + + 140 + 140 + + + + + 140 + 140 + + + + Media Control + + + + layout3 + + + + 0 + 10 + 120 + 120 + + + + + unnamed + + + + tQVolumeSlider + + + Vertical + + + NoMarks + + + + + tQProgressSlider + + + Horizontal + + + Below + + + + + pixmapAudio + + + image0 + + + true + + + + + layoutButtons + + + + unnamed + + + + tQPushButtonPlay + + + + 22 + 22 + + + + + 22 + 22 + + + + > + Play + + + + + + true + + + + + tQPushButtonSeekForward + + + + 22 + 22 + + + + + 22 + 22 + + + + >> + Seek Forward + + + + + + + + tQPushButtonForward + + + + 22 + 22 + + + + + 22 + 22 + + + + >| + Next + + + + + + + + tQPushButtonSeekBackward + + + + 22 + 22 + + + + + 22 + 22 + + + + << + Seek Back + + + + + + + + tQPushButtonBackward + + + + 22 + 22 + + + + + 22 + 22 + + + + |< + Previous + + + + + + + + tQPushButtonStop + + + + 22 + 22 + + + + + 22 + 22 + + + + - + Stop + + + + + + + + + + tQPushButtonRepeat + + + + + + image1 + + + + + + true + + + + + + + + 89504e470d0a1a0a0000000d4948445200000020000000200806000000737a7af40000000473424954080808087c0864880000013c494441545885edd44d8a22311887f19f3078010fe6091457e2815c097e5d4711575e4010541475a1961f65f52a3da4a9699a1eed81a1febba4429ee74dbd09458a1429f28f53fa415696c7fef553e06ab50a2a958a5eaff7fef1950219349b4dd06eb741abd58a16bd42208346a3118167b359eee2670a6450abd540a7d389c0cbe5f2650211b8dbed82d3e91481e7f3f9d30572c1c7e3118cc7e308bc5eaf9f26108187c321381c0e60341a81cd66f329f83b0219d4eb7530180cc07ebfc7ef8ac378b55a7d69d3af084415f7fbfd08349d4ea3f162b10097cbe5af05a2eb14ba3a1c750087a30ef301fc783cbe2d900bfed85ca1e200de6eb7e07ebf835229ff954fd3f453812cfce38fe0d05cb7db0dec76bb77709aa6cee7b324496459e67abd4a922457205ccf3f09984c26a05c2ee76e50a4489122ff5dde0051dcb85173c955470000000049454e44ae426082 + + + 89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000000473424954080808087c0864880000030d49444154388dedd55b4814511807f0ff9999bdccec2415ae9bb8ed206e51b151490f5eb6d8cca8a7e862659715840a89ae04fa20928291866117e8c5a790a82028eaa17a88322c82a848964d0da92ca4a0dad471e7d4ceecd743bab86169906f7df03d9c0bbf73f8387c871111a623846951ffc3e3431a3f608c89001c00c4291e9a046001f84644d6843063cc595357332fb422d862936c45494aaa93a982200c9b09abf3c1fd7bd58cb13e2232d2e0402060afae3e12280d85eecef5fa94acac3992ec94c1184b4b207dccb931a3fffddb3562a9f0c834ad924020d0158944bea7e0bcbcbcccc260715376768ea2aa19e2d3e74f74ce8d496f2c3b157de18245ae2cb7472e0c16357577f755001848c19aa6cdb49256414e768ef4f071a76e18c69eb28ddb2e4f065fbd76a5fcd98ba76df94b97abaffa7a0b344d9b39060b00e0f57a338c78dce574ca88c5bea8534101a06ce3b6cb5f6331d566b3c1881b2eafd79b91aa3f00f8fd7e99733e5a373e1533159c735856029c73f8fd7e796c5e02009fcf270f3ce907f067f8fcf9b34126b23b8c61b86aef813963b0699ae09cc3b7d897820500d0340d969534e2c608ec36bbded2da5cfe2b7aea54739089ecd6d62ddb157d64c403002dadcde5a228ea834383304dd3d0340d69b0dbedd615598946225d66c9aa5297c36e6f6b3c514f4d271b3f00c0f1e686a060136f55842b55d929431004de78a29e24496a5bb62cdfd5dbdb6329b21275bbdd7a5a290044d7ac5ed77ef1d285854585c58e1de56175d6acd938d650eb01b0548070a96cd3565512258cc475ecda59e14c9809c4625fd4972fa3564f77f7f75d3b2adb0144c76036ae1f075ebfee5b77e3e6f5f0a7cf9fe673ce9d8aac0c35d41f3ffaa0f3bebfa3a3e3f0a183471cc9a489b3e75a13c3c3bacd6e77189999993d1bd66fbe989b9b7b1b4024550b221a9f1e225a4b446122aa22a2dd44944f44a17b1d775b6aeb6a78ffbb37b4ff60158dae8747f77b7e71d29b10808f00ee4cf020586865892832493c7da6759fa2b8bee167036affdd0b627ff1353100c500960020009d00bafe05fc57316d8dfe07fa3155dcf5eed88b0000000049454e44ae426082 + + + + diff --git a/src/tdebluez/mediaplayer.ui b/src/tdebluez/mediaplayer.ui new file mode 100644 index 0000000..f3c796b --- /dev/null +++ b/src/tdebluez/mediaplayer.ui @@ -0,0 +1,253 @@ + +MediaCtlDlg + + + MediaCtlDlg + + + + 0 + 0 + 257 + 178 + + + + Media Control + + + + layout3 + + + + 0 + 10 + 247 + 159 + + + + + unnamed + + + + tQVolumeSlider + + + Vertical + + + NoMarks + + + + + tQProgressSlider + + + Horizontal + + + Below + + + + + pixmapAudio + + + image0 + + + true + + + + + layoutButtons + + + + unnamed + + + + tQPushButtonPlay + + + + 22 + 22 + + + + + 32 + 32 + + + + > + Play + + + + + + true + + + + + tQPushButtonSeekForward + + + + 22 + 22 + + + + + 31 + 32 + + + + >> + Seek Forward + + + + + + + + tQPushButtonForward + + + + 22 + 22 + + + + + 32 + 32 + + + + >| + Next + + + + + + + + tQPushButtonSeekBackward + + + + 22 + 22 + + + + + 32 + 32 + + + + << + Seek Back + + + + + + + + tQPushButtonBackward + + + + 22 + 22 + + + + + 32 + 32 + + + + |< + Previous + + + + + + + + tQPushButtonStop + + + + 22 + 22 + + + + + 32 + 32 + + + + - + Stop + + + + + + + + + + tQPushButtonRepeat + + + + + + image1 + + + + + + true + + + + + + + + 89504e470d0a1a0a0000000d4948445200000020000000200806000000737a7af40000000473424954080808087c0864880000013c494441545885edd44d8a22311887f19f3078010fe6091457e2815c097e5d4711575e4010541475a1961f65f52a3da4a9699a1eed81a1febba4429ee74dbd09458a1429f28f53fa415696c7fef553e06ab50a2a958a5eaff7fef1950219349b4dd06eb741abd58a16bd42208346a3118167b359eee2670a6450abd540a7d389c0cbe5f2650211b8dbed82d3e91481e7f3f9d30572c1c7e3118cc7e308bc5eaf9f26108187c321381c0e60341a81cd66f329f83b0219d4eb7530180cc07ebfc7ef8ac378b55a7d69d3af084415f7fbfd08349d4ea3f162b10097cbe5af05a2eb14ba3a1c750087a30ef301fc783cbe2d900bfed85ca1e200de6eb7e07ebf835229ff954fd3f453812cfce38fe0d05cb7db0dec76bb77709aa6cee7b324496459e67abd4a922457205ccf3f09984c26a05c2ee76e50a4489122ff5dde0051dcb85173c955470000000049454e44ae426082 + + + 89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000000473424954080808087c0864880000030d49444154388dedd55b4814511807f0ff9999bdccec2415ae9bb8ed206e51b151490f5eb6d8cca8a7e862659715840a89ae04fa20928291866117e8c5a790a82028eaa17a88322c82a848964d0da92ca4a0dad471e7d4ceecd743bab86169906f7df03d9c0bbf73f8387c871111a623846951ffc3e3431a3f608c89001c00c4291e9a046001f84644d6843063cc595357332fb422d862936c45494aaa93a982200c9b09abf3c1fd7bd58cb13e2232d2e0402060afae3e12280d85eecef5fa94acac3992ec94c1184b4b207dccb931a3fffddb3562a9f0c834ad924020d0158944bea7e0bcbcbcccc260715376768ea2aa19e2d3e74f74ce8d496f2c3b157de18245ae2cb7472e0c16357577f755001848c19aa6cdb49256414e768ef4f071a76e18c69eb28ddb2e4f065fbd76a5fcd98ba76df94b97abaffa7a0b344d9b39060b00e0f57a338c78dce574ca88c5bea8534101a06ce3b6cb5f6331d566b3c1881b2eafd79b91aa3f00f8fd7e99733e5a373e1533159c735856029c73f8fd7e796c5e02009fcf270f3ce907f067f8fcf9b34126b23b8c61b86aef813963b0699ae09cc3b7d897820500d0340d969534e2c608ec36bbded2da5cfe2b7aea54739089ecd6d62ddb157d64c403002dadcde5a228ea834383304dd3d0340d69b0dbedd615598946225d66c9aa5297c36e6f6b3c514f4d271b3f00c0f1e686a060136f55842b55d929431004de78a29e24496a5bb62cdfd5dbdb6329b21275bbdd7a5a290044d7ac5ed77ef1d285854585c58e1de56175d6acd938d650eb01b0548070a96cd3565512258cc475ecda59e14c9809c4625fd4972fa3564f77f7f75d3b2adb0144c76036ae1f075ebfee5b77e3e6f5f0a7cf9fe673ce9d8aac0c35d41f3ffaa0f3bebfa3a3e3f0a183471cc9a489b3e75a13c3c3bacd6e77189999993d1bd66fbe989b9b7b1b4024550b221a9f1e225a4b446122aa22a2dd44944f44a17b1d775b6aeb6a78ffbb37b4ff60158dae8747f77b7e71d29b10808f00ee4cf020586865892832493c7da6759fa2b8bee167036affdd0b627ff1353100c500960020009d00bafe05fc57316d8dfe07fa3155dcf5eed88b0000000049454e44ae426082 + + + + diff --git a/src/tdebluez/tdebluez.autostart.desktop b/src/tdebluez/tdebluez.autostart.desktop new file mode 100644 index 0000000..4ac8608 --- /dev/null +++ b/src/tdebluez/tdebluez.autostart.desktop @@ -0,0 +1,44 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=TDEBluetooth +Name[sv]=TDEBluetooth +GenericName=TDE Bluetooth Daemon +GenericName[ar]=مراقب TDE Bluetooth +GenericName[bg]=Демон за Bluetooth в TDE +GenericName[br]=Diaoul TDE Bluetooth +GenericName[bs]=TDE Bluetooth daemon +GenericName[ca]=Dimoni Bluetooth de TDE +GenericName[cs]=Bluetooth démon pro TDE +GenericName[da]=TDE Bluetooth-dæmon +GenericName[de]=TDE Bluetooth-Daemon +GenericName[el]=Ο δαίμονας Bluetooth του TDE +GenericName[es]=Daemon Bluetooth de TDE +GenericName[et]=TDE Bluetoothi deemon +GenericName[fr]=Démon TDE Bluetooth +GenericName[gl]=Servizo Bluetooth de TDE +GenericName[it]=Demone Bluetooth di TDE +GenericName[ja]=TDE Bluetooth デーモン +GenericName[lt]=TDE Bluetooth tarnyba +GenericName[nl]=TDE Bluetooth-daemon +GenericName[pa]=TDE ਬਲਿਊਟੁੱਥ ਡਾਈਮੋਨ +GenericName[pl]=Demon Bluetooth dla TDE +GenericName[pt]=Serviço Bluetooth do TDE +GenericName[pt_BR]=Serviço Bluetooth do TDE +GenericName[ru]=Служба TDE Bluetooth +GenericName[sr]=Bluetooth демон за TDE +GenericName[sr@Latn]=Bluetooth demon za TDE +GenericName[sv]=TDE-Blåtandsdemon +GenericName[ta]=TDE புலுடுத் டேமொன் +GenericName[tg]=Даймони TDE Bluetooth +GenericName[tr]=TDE Bluetooth Servisi +Exec=tdebluez +Icon=tdebluez +Type=Application +X-DocPath=tdebluez/index.html +Terminal=false +X-TDE-autostart-after=panel +X-TDE-StartupNotify=false +X-DCOP-ServiceType=Unique +#X-TDE-Library=klipper_panelapplet +#X-TDE-UniqueApplet=true +X-TDE-autostart-condition=tdebluezrc:General:AutoStart:false diff --git a/src/tdebluez/tdebluez.desktop b/src/tdebluez/tdebluez.desktop new file mode 100644 index 0000000..10c037f --- /dev/null +++ b/src/tdebluez/tdebluez.desktop @@ -0,0 +1,61 @@ +[Desktop Entry] +Encoding=UTF-8 +Exec=tdebluez +Icon=tdebluez +Type=Application +Name=tdebluez +Name[en]=tdebluez +GenericName=Bluetooth Server +GenericName[ar]=خادم Bluetooth +GenericName[bg]=Сървър за Bluetooth +GenericName[br]=Servijer Bluetooth +GenericName[ca]=Servidor Bluetooth +GenericName[cs]=Bluetooth server +GenericName[da]=Bluetooth-server +GenericName[de]=Bluetooth-Server +GenericName[el]=Εξυπηρετητής Bluetooth +GenericName[es]=Servidor Bluetooth +GenericName[et]=Bluetoothi server +GenericName[fr]=Services Bluetooth +GenericName[ga]=Freastalaí Bluetooth +GenericName[gl]=Servidor Bluetooth +GenericName[it]=Server Bluetooth +GenericName[ja]=Bluetooth サーバ +GenericName[lt]=Bluetooth serveris +GenericName[nl]=Bluetooth-dienst +GenericName[pa]=ਬਲਿਊਟੁੱਥ ਸਰਵਰ +GenericName[pl]=Serwer Bluetooth +GenericName[pt]=Servidor Bluetooth +GenericName[pt_BR]=Servidor Bluetooth +GenericName[sr]=Bluetooth сервер +GenericName[sr@Latn]=Bluetooth server +GenericName[sv]=Blåtandserver +GenericName[tg]=Хидматгоҳи Bluetooth +GenericName[tr]=Bluetooth Sunucusu +GenericName[xx]=xxBluetooth Serverxx +Categories=Qt;TDE;System;Monitor; +OnlyShowIn=TDE; +Comment=TDE Bluetooth Framework Metaserver +Comment[bg]=Мета сървър за Bluetooth Framework +Comment[bs]=TDE Bluetooth framework metaserver +Comment[ca]=Meta-servidor de l'estructura Bluetooth de TDE +Comment[da]=TDE Bluetooth-skelet metaserver +Comment[de]=TDE Bluetooth-Framework Metaserver +Comment[el]=Πλαίσιο στήριξης εξυπηρετητή Bluetooth του TDE +Comment[es]=Metaservidor del marco de trabajo Bluetooth de TDE +Comment[et]=TDE Bluetooth raamistiku metaserver +Comment[fr]=Méta-serveur de l'interface TDE Bluetooth +Comment[gl]=Meta-servidor da Infra-estrutura Bluetooth de TDE +Comment[it]=Metaserver dell'infrastruttura Bluetooth di TDE +Comment[ja]=TDE Bluetooth フレームワーク メタサーバ +Comment[lt]=TDE Bluetooth aplinkos meta serveris +Comment[pl]=Metaserwer szkieletu TDE Bluetooth +Comment[pt]=Meta-servidor da Infra-estrutura Bluetooth do TDE +Comment[pt_BR]=Meta-servidor da Infra-estrutura Bluetooth do TDE +Comment[sr]=Bluetooth оквирни метасервер за TDE +Comment[sr@Latn]=Bluetooth okvirni metaserver za TDE +Comment[sv]=Metaserver för TDE:s Blåtandsramverk +Comment[ta]=TDE புலுடுத் சட்ட வேலை மேட்டா சேவகன் +Comment[tr]=TDE Bluetooth Ortamı Meta Sunucusu +Comment[xx]=xxTDE Bluetooth Framework Metaserverxx + diff --git a/src/tdebluez/trayicon.cpp b/src/tdebluez/trayicon.cpp new file mode 100644 index 0000000..3f56194 --- /dev/null +++ b/src/tdebluez/trayicon.cpp @@ -0,0 +1,806 @@ +/* + * + * TrayIcon for tdebluez + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of tdebluez. + * + * tdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * tdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include + +#include "application.h" + +#include "trayicon.h" + +#define PIXMAP_LOGO "tdebluez" +#define HELP_DOCUMENT "tdebluez/components.html#components.TDEBluetooth" +#define PROCESS_KINPUTWIZARD "kinputwizard" +#define PROCESS_KBLUEMON "kbluemon" +#define PROCESS_KBLUELOCK "kbluelock" + + +using namespace TDEBluetooth; + +TrayIcon::TrayIcon(TDEBluetoothApp* _app) +{ + app = _app; + + iconConnectingBlinkState = false; + helpMenu = new KHelpMenu(this, TDEApplication::kApplication()->aboutData()); + + adapterConfigDialog = new AdapterConfigDialog(app); + deviceWizard = new DeviceWizard(app); + +// alwaysVisible was part of the old code and is to be decided if we need it in the future +// the idea is that the tray icon may be invisible if desired +// config->setGroup("UI"); +// alwaysVisible = config->readBoolEntry("alwaysVisible", true); + + bool agentstate = app->getStartAuthAgent(); + bool obexserver = app->getStartObex(); + bool autostart = app->getAutoStart(); + +// blinkTimer = new TQTimer(this); +// connect(blinkTimer, SIGNAL(timeout()), this, TQT_SLOT(slotSearchIcon())); + +// This following commented out lines are also part of the old code +// They represent features that were not migrated to Bluez5 +// KBlueMon +//// kbluemonAction = new TDEAction(tr("KBlue&Mon"), +//// TDEGlobal::iconLoader()->loadIcon("info", TDEIcon::Small, 16), +//// TDEShortcut::null(), this, "kbluemon"); +//// connect(kbluemonAction, SIGNAL(activated()), this, SLOT(slotkbluemonitor())); +//// +//// // KBlueLock +//// kbluelockAction = new TDEAction(tr("KBlue&Lock"), +//// TDEGlobal::iconLoader()->loadIcon("kbluelock", TDEIcon::Small, 16), +//// TDEShortcut::null(), this, "kbluelock"); +//// connect(kbluelockAction, SIGNAL(activated()), this, SLOT(slotkbluelock())); +//// +//// // KBtobexclient +//// kbtobexclientAction = new TDEAction(tr("&Send File..."), +//// TDEGlobal::iconLoader()->loadIcon("attach", TDEIcon::Small, 16), +//// TDEShortcut::null(), this, "kbtobexclient"); +//// connect(kbtobexclientAction, SIGNAL(activated()), this, SLOT(slotkbtobexclient())); +//// +// // BT switch + bool anyPowered = false; + TDEBluetoothApp::AdaptersMap::iterator it = app->adapters.begin(); + for (it; it != app->adapters.end(); ++it) + { + // slotAddAdapter(it.key()); + TQT_DBusError dbuserr; + bool powered = app->adapters[it.key()]->getPowered(dbuserr); + if (dbuserr.isValid()) + tqDebug("Adapter getPowered failed: %s", dbuserr.message().local8Bit().data()); + TQString name = app->adapters[it.key()]->getAlias(dbuserr); + if (dbuserr.isValid()) + tqDebug("Adapter getAlias failed: %s", dbuserr.message().local8Bit().data()); + + TQString msg; + if (powered) { + anyPowered = powered; + msg = tr("Power off [%1]").arg(name); + } + else + { + msg = tr("Power on [%1]").arg(name); + } + + TDEToggleAction *ad_a = new TDEToggleAction(this, name.utf8()); + ad_a->setText(msg); + ad_a->setChecked(powered); + + showPowerOnActionMap.insert(it.key(), ad_a); + connect(ad_a, SIGNAL(toggled(bool)), this, TQT_SLOT(slotPowerOn(bool))); + + } + + // Configuration menu + configActionMenu = new TDEActionMenu(tr("&Configuration"), TDEGlobal::iconLoader()->loadIcon("configure", TDEIcon::Small, 16), this, "config_menu"); + + // Help menu + helpActionMenu = new TDEActionMenu(tr("&Help"), TDEGlobal::iconLoader()->loadIcon("help", TDEIcon::Small, 16), this, "help_menu"); + + // show documentation + showHelpAction = KStdAction::help(this, TQT_SLOT(slotShowHelp()), actionCollection()); + + // "About" menu item + aboutAction = KStdAction::aboutApp(this, TQT_SLOT(slotAbout()), actionCollection()); + +// // Inputdevice configuration +// inputConfigAction = new TDEAction(tr("&Input Devices..."), +// TDEGlobal::iconLoader()->loadIcon("configure", TDEIcon::Small, 16), +// TDEShortcut::null(), this, "input_config"); +// connect(inputConfigAction, SIGNAL(activated()), this, SLOT(slotInputConfig())); + + // Bluetooth Device configuration + // from 06_rev748007.patch + deviceConfigAction = new TDEAction(tr("&Devices..."), TDEGlobal::iconLoader()->loadIcon("configure", TDEIcon::Small, 16), TDEShortcut::null(), this, "device_config"); + connect(deviceConfigAction, SIGNAL(activated()), this, TQT_SLOT(slotDeviceConfig())); + + // Bluetooth Adapter configuration + adapterConfigAction = new TDEAction(tr("&Adapters..."), TDEGlobal::iconLoader()->loadIcon("configure", TDEIcon::Small, 16), TDEShortcut::null(), this, "adapter_config"); + connect(adapterConfigAction, SIGNAL(activated()), this, TQT_SLOT(slotAdapterConfig())); + +// // Paired device configuration +// pairedConfigAction = new TDEAction(tr("&Paired/Trusted Devices..."), +// TDEGlobal::iconLoader()->loadIcon("configure", TDEIcon::Small, 16), +// TDEShortcut::null(), this, "paired_config"); +// connect(pairedConfigAction, SIGNAL(activated()), this, SLOT(slotPairedConfig())); + + // Autostart switch + autostartIconAction = new TDEToggleAction(this, "autostart"); + connect(autostartIconAction, SIGNAL(toggled(bool)), this, TQT_SLOT(slotIconAutostartToggled(bool))); + autostartIconAction->setChecked(autostart); + autostartIconAction->setText(tr("&Auto Start")); + + // Agent switch + agentIconAction = new TDEToggleAction(this, "tdeauthagent"); + connect(agentIconAction, SIGNAL(toggled(bool)), this, TQT_SLOT(slotIconAgentToggled(bool))); + agentIconAction->setChecked(agentstate); + agentIconAction->setText(tr("Auth A&gent")); + + // Obexsrv switch + obexIconAction = new TDEToggleAction(this, "tdebtobexsrv"); + connect(obexIconAction, SIGNAL(toggled(bool)), this, TQT_SLOT(slotIconObexToggled(bool))); + obexIconAction->setChecked(obexserver); + obexIconAction->setText(tr("&Obex Server")); + + slotIconAgentToggled(agentstate); + slotIconObexToggled(obexserver); + +// // Always-visible switch +// showIconAction = new TDEToggleAction(this, "always_visible"); +// connect(showIconAction, SIGNAL(toggled(bool)), this, SLOT(slotShowIconToggled(bool))); +// showIconAction->setChecked(alwaysVisible); +// showIconAction->setText(tr("Always &Visible")); +// configActionMenu->insert(showIconAction); + + KPixmap logoPixmap = TDEGlobal::iconLoader()->loadIcon(PIXMAP_LOGO, TDEIcon::Small, 22); + iconIdle = logoPixmap; + iconOff = logoPixmap; + iconConnected = logoPixmap; + iconNoAdapter = logoPixmap; + KPixmapEffect::toGray(iconOff); + KPixmapEffect::fade(iconOff, 0.3, TQColor(255, 255, 255)); + KPixmapEffect::fade(iconConnected, 0.5, TQColor(255, 0, 0)); + KPixmapEffect::toGray(iconNoAdapter); + KPixmapEffect::fade(iconNoAdapter, 0.5, TQColor(128, 128, 128)); + + setupTray(anyPowered); + + connect(app, SIGNAL(signalAdapterAdded(const TQString&)), this, TQT_SLOT(slotAddAdapter(const TQString&))); + connect(app, SIGNAL(signalAdapterRemoved(const TQString&)), this, TQT_SLOT(slotRemoveAdapter(const TQString&))); + + connect(app->manager, SIGNAL(adapterPowerOnChanged(const TQString&, bool)), this, TQT_SLOT(slotPowerOnChanged(const TQString&, bool))); + connect(app->manager, SIGNAL(adapterNameChanged(const TQString&, const TQString&)), this, TQT_SLOT(slotUpdateToolTip(const TQString&, const TQString&))); + connect(app->manager, SIGNAL(adapterAliasChanged(const TQString&, const TQString&)), this, TQT_SLOT(slotUpdateToolTip(const TQString&, const TQString&))); + connect(app->manager, SIGNAL(adapterNameChanged(const TQString&, const TQString&)), this, TQT_SLOT(slotAdapterAliasChanged(const TQString&, const TQString&))); + connect(app->manager, SIGNAL(adapterAliasChanged(const TQString&, const TQString&)), this, TQT_SLOT(slotAdapterAliasChanged(const TQString&, const TQString&))); + +// connect(app->manager, SIGNAL(adapterModeChanged(const TQString&, const TQString&)), this, TQT_SLOT(slotUpdateToolTip(const TQString&, const TQString&))); +// connect(app->manager, SIGNAL(adapterPowerOnChanged(const TQString&, bool)), this, TQT_SLOT(slotPowerOnChanged(const TQString&, bool))); + connect(app->manager, SIGNAL(adapterDiscoverableTimeoutChanged(const TQString&, TQ_UINT32)), + this, TQT_SLOT(slotDiscoverableTimeoutChanged(const TQString&, TQ_UINT32))); + connect(app->manager, SIGNAL(adapterDiscoverableChanged(const TQString&, bool)), + this, TQT_SLOT(slotDiscoverableChanged(const TQString&, bool))); +// connect(app->manager, SIGNAL(adapterDiscoveringChanged(const TQString&, bool)), this, TQT_SLOT(slotDiscoveringChanged(const TQString&, bool))); + connect(app->manager, SIGNAL(deviceConnectedChanged(const TQString&, bool)), this, TQT_SLOT(slotDeviceConnectedChanged(const TQString&, bool))); + + connect(adapterConfigDialog, SIGNAL(signalAdapterSelected(const TQString&, const TQString&)), + deviceWizard, TQT_SLOT(slotSetAdapter(const TQString&, const TQString&))); + connect(adapterConfigDialog, SIGNAL(signalAdapterSelected(const TQString&, const TQString&)), + this, TQT_SLOT(slotUpdateToolTip(const TQString&, const TQString&))); + + connect(this, SIGNAL(quitSelected()), this, SLOT(slotQuitSelected())); + +} + +TrayIcon::~TrayIcon() +{ + +// disconnect(app->manager, SIGNAL(deviceConnectedChanged(const TQString&, bool)), this, TQT_SLOT(slotDeviceConnectedChanged(const TQString&, bool))); +// disconnect(app->manager, SIGNAL(adapterNameChanged(const TQString&, const TQString&)), this, TQT_SLOT(slotUpdateToolTip(const TQString&, const TQString&))); +// disconnect(app->manager, SIGNAL(adapterAliasChanged(const TQString&, const TQString&)), this, TQT_SLOT(slotUpdateToolTip(const TQString&, const TQString&))); +//// disconnect(app->manager, SIGNAL(adapterModeChanged(const TQString&, const TQString&)), this, SLOT(slotUpdateToolTip(const TQString&, const TQString&))); +//// disconnect(app->manager, SIGNAL(adapterPowerOnChanged(const TQString&, bool)), this, TQT_SLOT(slotPowerOnChanged(const TQString&, bool))); +// disconnect(app->manager, SIGNAL(adapterDiscoverableTimeoutChanged(const TQString&, TQ_UINT32)), +// this, SLOT(slotDiscoverableTimeoutChanged(const TQString&, TQ_UINT32))); +// disconnect(app->manager, SIGNAL(adapterDiscoverableChanged(const TQString&, bool)), +// this, SLOT(slotDiscoverableChanged(const TQString&, bool))); +//// disconnect(app->manager, SIGNAL(adapterDiscoveringChanged(const TQString&, bool)), this, SLOT(slotDiscoveringChanged(const TQString&, bool))); + + if (deviceWizard) + delete deviceWizard; + if (adapterConfigDialog) + delete adapterConfigDialog; + + delete obexIconAction; + delete agentIconAction; + delete autostartIconAction; + delete adapterConfigAction; + delete deviceConfigAction; + delete helpActionMenu; + delete configActionMenu; + delete helpMenu; +} + +void TrayIcon::setupTray(bool enable) +{ + + // CONTEXT MENU + + // if (!kbluemonAction->isPlugged()) + // kbluemonAction->plug(contextMenu()); + // kbluemonAction->setEnabled(enable); + // + // if (!kbluelockAction->isPlugged()) + // kbluelockAction->plug(contextMenu()); + // kbluelockAction->setEnabled(enable); + // + // if (!kbtobexclientAction->isPlugged()) + // kbtobexclientAction->plug(contextMenu()); + // kbtobexclientAction->setEnabled(enable); + + TQMap::iterator it = showPowerOnActionMap.begin(); + for (it; it != showPowerOnActionMap.end(); ++it) + { + if (!it.data()->isPlugged()) + it.data()->plug(contextMenu(), 1); + // The toggle should always be active in order to power on and off + it.data()->setEnabled(true); + } + + // device wizard + if (!deviceConfigAction->isPlugged()) + deviceConfigAction->plug(contextMenu()); + deviceConfigAction->setEnabled(enable); + + // adapter configuration + if (!adapterConfigAction->isPlugged()) + adapterConfigAction->plug(contextMenu()); + adapterConfigAction->setEnabled(enable); + + // the config menu + if (!configActionMenu->isPlugged()) + configActionMenu->plug(contextMenu()); + if (!showPowerOnActionMap.isEmpty()) + configActionMenu->setEnabled(true); + else + configActionMenu->setEnabled(false); + + // the help menu + if (!helpActionMenu->isPlugged()) + helpActionMenu->plug(contextMenu()); + + // the help sub menu + if (!showHelpAction->isPlugged()) + showHelpAction->plug(helpActionMenu->popupMenu()); + + // Report bug menu item + /* + TDEAction* reportBugAction = KStdAction::reportBug(this, + SLOT(slotReportBug()), actionCollection()); + helpActionMenu->insert(reportBugAction); + */ + + // ABOUT MENU + if (!aboutAction->isPlugged()) + aboutAction->plug(helpActionMenu->popupMenu()); + + // Menu entries: + // ------------- + + // // CONFIG MENU + // if (!inputConfigAction->isPlugged()) + // inputConfigAction->plug(configActionMenu->popupMenu()); + + // Service configuration + /* TODO: create BlueZ service UI + serviceConfigAction = new TDEAction(tr("Configure &Services..."), + TDEGlobal::iconLoader()->loadIcon("configure", TDEIcon::Small, 16), + TDEShortcut::null(), this, "service_config"); + connect(serviceConfigAction, SIGNAL(activated()), this, SLOT(slotServiceConfig())); + configActionMenu->insert(serviceConfigAction); + */ + + // if (!pairedConfigAction->isPlugged()) + // pairedConfigAction->plug(configActionMenu->popupMenu()); + // configure notifications + /* + TDEAction* notificationAction = KStdAction::configureNotifications(this, + SLOT(slotConfigureNotifications()), actionCollection()); + configActionMenu->insert(notificationAction); + */ + + // Autostart option + if (!autostartIconAction->isPlugged()) + autostartIconAction->plug(configActionMenu->popupMenu()); + + // Agent option + if (!agentIconAction->isPlugged()) + agentIconAction->plug(configActionMenu->popupMenu()); + + //Obex option + if (!obexIconAction->isPlugged()) + obexIconAction->plug(configActionMenu->popupMenu()); + + // if (!showIconAction->isPlugged()) + // showIconAction->plug(configActionMenu->popupMenu()); + + updateIcon(); + show(); +} + +void TrayIcon::updateIcon() +{ + TQString oldText = TQToolTip::textFor(this); + if (showPowerOnActionMap.isEmpty()) + { + setPixmap(iconNoAdapter); + updateTooltip(tr("No BT adapter")); + if (!this->isHidden()) + KNotifyClient::event(TDEApplication::kApplication()->mainWidget()->winId(), + "AdapterDetached", tr("No BT adapter available.
(BT adapter removed)")); + + adapterConfigAction->setEnabled(false); + deviceConfigAction->setEnabled(false); + return; + } + + TQT_DBusError dbuserr; + bool anyPowered = false; + TQString name; + TQString path; + TQMap::iterator it = showPowerOnActionMap.begin(); + for (it; it != showPowerOnActionMap.end(); ++it) + { + anyPowered = app->adapters[it.key()]->getPowered(dbuserr); + if (dbuserr.isValid()) + tqDebug("Adapter getPowered failed: %s", dbuserr.message().local8Bit().data()); + // know if any adapter is powered + if (anyPowered) + { + path = app->adapters[it.key()]->getPath(); + name = app->adapters[it.key()]->getAlias(dbuserr); + if (dbuserr.isValid()) + tqDebug("Adapter getAlias failed: %s", dbuserr.message().local8Bit().data()); + break; + } + } + + if (anyPowered) + { + bool anyConnected = false; + TDEBluetoothApp::DevicesMap::Iterator dit = app->devices.begin(); + for (dit; dit != app->devices.end(); ++dit) + { + anyConnected = dit.data()->getConnected(dbuserr); + if (dbuserr.isValid()) + tqDebug("Check for connected device failed: %s", dbuserr.message().local8Bit().data()); + if (anyConnected) + break; + } + + if (anyConnected) + setPixmap(iconConnected); + else + setPixmap(iconIdle); + } + else + { + if (deviceWizard) + deviceWizard->hide(); + + if (adapterConfigDialog) + adapterConfigDialog->hide(); + + //if no other adapter was powered, use the first one for the tooltip + path = app->adapters.begin().data()->getPath(); + name = app->adapters.begin().data()->getAlias(dbuserr); + if (dbuserr.isValid()) + tqDebug("Adapter getAlias failed: %s", dbuserr.message().local8Bit().data()); + + setPixmap(iconOff); + } + + slotUpdateToolTip(path, name); + + adapterConfigAction->setEnabled(anyPowered); + deviceConfigAction->setEnabled(anyPowered); +} + +void TrayIcon::slotIconAutostartToggled(bool state) +{ + app->setAutoStart(state); +} + +void TrayIcon::slotServiceConfig() +{ + kdDebug() << k_funcinfo << endl; +} + +void TrayIcon::slotAdapterConfig() +{ + adapterConfigDialog->show(); + adapterConfigDialog->raise(); +} + +void TrayIcon::slotDeviceConfig() +{ + deviceWizard->show(); + deviceWizard->raise(); + deviceWizard->setFocus(); +} + +void TrayIcon::slotInputConfig() +{ + TDEProcess process(0); + + process << PROCESS_KINPUTWIZARD; + if (!process.start(TDEProcess::DontCare)) + { + KMessageBox::information(this, tr("Could not execute kinputwizard."), tr("TDEBluetooth")); + } +} + +void TrayIcon::slotkbluemonitor() +{ + TDEProcess process(0); + process << PROCESS_KBLUEMON; + + if (!process.start(TDEProcess::DontCare)) + { + KMessageBox::information(this, tr("Could not execute KBlueMon."), tr("TDEBluetooth")); + } +} + +void TrayIcon::slotkbluelock() +{ + TDEProcess process(0); + process << PROCESS_KBLUELOCK; + + if (!process.start(TDEProcess::DontCare)) + { + KMessageBox::information(this, tr("Could not execute KBlueLock."), tr("TDEBluetooth")); + } +} + +void TrayIcon::slotIconObexToggled(bool state) +{ + app->setStartObex(state); + + if (state) + { + if (!app->startObexSrv()) + { + KMessageBox::information(this, tr("Could not start OBEX server."), tr("TDEBluetooth")); + } + } + else + { + if (!app->stopObexSrv()) + { + KMessageBox::information(this, tr("Could not stop OBEX server."), tr("TDEBluetooth")); + } + } +} + +void TrayIcon::slotIconAgentToggled(bool state) +{ + app->setStartAuthAgent(state); + + if (state) + { + if (!app->startAuthAgent()) + { + KMessageBox::information(this, tr("Could not start TDEBluez Authentication Agent."), tr("TDEBluetooth")); + } + } + else + { + if (!app->stopAuthAgent()) + { + KMessageBox::information(this, tr("Could not stop TDEBluez Authentication Agent."), tr("TDEBluetooth")); + } + } +} + +void TrayIcon::slotPowerOn(bool state) +{ + // who send the signal ? + const TQObject * o = TQObject::sender(); + const TDEToggleAction* obj = const_cast(reinterpret_cast(o)); + TQMap::Iterator it = showPowerOnActionMap.begin(); + + TQString path; + for (it; it != showPowerOnActionMap.end(); ++it) + { + if (obj == it.data()) + { + path = it.key(); + break; + } + } + + app->adapters[path]->powerOn(state); +} + +void TrayIcon::slotPowerOnChanged(const TQString &adapter, bool state) +{ + TQT_DBusError dbuserr; + TQString name = app->adapters[adapter]->getAlias(dbuserr); + if (dbuserr.isValid()) + tqDebug("Adapter getAlias failed: %s", dbuserr.message().local8Bit().data()); + + TQString addr = app->adapters[adapter]->getAddress(dbuserr); + if (dbuserr.isValid()) + tqDebug("Adapter getAddress failed: %s", dbuserr.message().local8Bit().data()); + + if (name.isEmpty() && addr.isEmpty()) // adapter was removed + return; + + TQString msg; + if (state) + { + KNotifyClient::event(TDEApplication::kApplication()->mainWidget()->winId(), + "AdapterAvailable", tr("BT adapter %1 is on
(%2)").arg(name).arg(addr)); + msg = tr("Power off [%1]").arg(name); + } + else + { + KNotifyClient::event(TDEApplication::kApplication()->mainWidget()->winId(), + "AdapterAvailable", tr("BT adapter %1 is off
(%2)").arg(name).arg(addr)); + msg = tr("Power on [%1]").arg(name); + } + + showPowerOnActionMap[adapter]->setText(msg); + showPowerOnActionMap[adapter]->setChecked(state); + slotUpdateToolTip(adapter, name); + updateIcon(); +} + +void TrayIcon::slotDeviceConnectedChanged(const TQString& path, bool state) +{ + TQT_DBusError dbuserr; + TQString name = app->devices[path]->getAlias(dbuserr); + if (dbuserr.isValid()) + tqDebug("Device getAlias failed: %s", dbuserr.message().local8Bit().data()); + TQString address = app->devices[path]->getAddress(dbuserr); + if (dbuserr.isValid()) + tqDebug("Adapter getAddress failed: %s", dbuserr.message().local8Bit().data()); + if (state) + { + KNotifyClient::event(TDEApplication::kApplication()->mainWidget()->winId(), + "IncomingConnection", tr("Connected to %1 (%2)").arg(name).arg(address)); + } + else + { + KNotifyClient::event(TDEApplication::kApplication()->mainWidget()->winId(), + "IncomingConnection", tr("Disconnected from %1 (%2)").arg(name).arg(address)); + } + updateIcon(); +} + +TQString TrayIcon::localAndEnglish(const TQCString& s) +{ + if (TQString(s) != tr(s)) + return TQString("%1 (\"%2\")").arg(tr(s)).arg(s); + else + return s; +} + +void TrayIcon::mousePressEvent(TQMouseEvent *e) +{ + if (e->button() == TQMouseEvent::LeftButton) + { +// TQMessageBox::information(this, +// tr("Use right click to open the menu"), +// tr("Left click is reserved for ObexFTP browsing.\n" +// "ObexFTP is subject of development.")); + e->accept(); + TDEProcess proc; + proc << "kfmclient" << "openURL" << "bluetooth:/"; + proc.start(TDEProcess::DontCare); + } + else + { + KSystemTray::mousePressEvent(e); + } +} + +void TrayIcon::slotQuitSelected() +{ + // Ask if the user want to simply quit or disable + // automatic start of TDEBluetooth + if (!autostartIconAction->isChecked()) + { + int autoStart = KMessageBox::questionYesNoCancel(0, + tr("Should TDEBluetooth still be restarted when you login?"), + tr("Automatically Start TDEBluetooth?"), + tr("Start"), + tr("Do Not Start")); + + if (autoStart == KMessageBox::Yes) + app->setAutoStart(true); + else if (autoStart == KMessageBox::No) + app->setAutoStart(false); + } +} + +void TrayIcon::slotShowHelp() +{ + // TODO: This is surely not the correct way to jump to the + // right chapter. Do I really have to mention the html-file, + // or is the id enough? + TDEApplication::kApplication()->invokeHelp("", HELP_DOCUMENT); +} + +void TrayIcon::slotAddAdapter(const TQString& path) +{ + kdDebug() << k_funcinfo << endl; // for debugging MicheleC report + TQT_DBusError dbuserr; + + bool powered = app->adapters[path]->getPowered(dbuserr); + if (dbuserr.isValid()) + tqDebug("Adapter getPowered failed: %s", dbuserr.message().local8Bit().data()); + + TQString name = app->adapters[path]->getAlias(dbuserr); + if (dbuserr.isValid()) + tqDebug("Adapter getAlias failed: %s", dbuserr.message().local8Bit().data()); + + TQString addr = app->adapters[path]->getAddress(dbuserr); + if (dbuserr.isValid()) + tqDebug("Adapter getAddress failed: %s", dbuserr.message().local8Bit().data()); + + TQString msg; + if (powered) + msg = tr("Power off [%1]").arg(name); + else + msg = tr("Power on [%1]").arg(name); + + TDEToggleAction *ad_a = new TDEToggleAction(this, name.utf8()); + ad_a->setText(msg); + ad_a->setChecked(powered); + + showPowerOnActionMap.insert(path, ad_a); + connect(ad_a, SIGNAL(toggled(bool)), this, SLOT(slotPowerOn(bool))); + + KNotifyClient::event(TDEApplication::kApplication()->mainWidget()->winId(), + "AdapterAttached", tr("BT adapter %1 attached
(%2)").arg(name).arg(addr)); + + if(!powered) // find out if we have some powered adapter + { + TQMap::iterator it = showPowerOnActionMap.begin(); + for (it; it != showPowerOnActionMap.end(); ++it) + { + powered = app->adapters[it.key()]->getPowered(dbuserr); + if (dbuserr.isValid()) + tqDebug("Adapter getPowered failed: %s", dbuserr.message().local8Bit().data()); + // know if any adapter is powered + if (powered) + break; + } + } + + if (obexIconAction->isChecked()) + { + if (!app->obexServer->isRunning()) + app->startObexSrv(); + } + + if (agentIconAction->isChecked()) + { + if (!app->authAgent->isRunning()) + app->startAuthAgent(); + } + + setupTray(powered); +} + +void TrayIcon::slotRemoveAdapter(const TQString& path) +{ + kdDebug() << k_funcinfo << endl; // for debugging MicheleC report + disconnect(showPowerOnActionMap[path], SIGNAL(toggled(bool)), this, SLOT(slotPowerOn(bool))); + + if (showPowerOnActionMap[path]->isPlugged()) + showPowerOnActionMap[path]->unplug(contextMenu()); + showPowerOnActionMap.remove(path); + + KNotifyClient::event(TDEApplication::kApplication()->mainWidget()->winId(), + "AdapterDetached", tr("BT adapter detached")); + + if (showPowerOnActionMap.isEmpty()) + { + if (deviceWizard) + deviceWizard->hide(); + if (adapterConfigDialog) + adapterConfigDialog->hide(); + if (app->obexServer->isRunning()) + app->stopObexSrv(); + if (app->authAgent->isRunning()) + app->stopAuthAgent(); + setupTray(false); + } + else + { + setupTray(true); + } +} + +void TrayIcon::slotAdapterAliasChanged(const TQString &path, const TQString &alias) +{ + // slotAddAdapter(it.key()); + TQT_DBusError dbuserr; + bool powered = showPowerOnActionMap[path]->isChecked(); + TQString msg; + if (powered) + msg = tr("Power off [%1]").arg(alias); + else + msg = tr("Power on [%1]").arg(alias); + showPowerOnActionMap[path]->setText(msg); +} + +void TrayIcon::slotDiscoverableTimeoutChanged(const TQString &path, TQ_UINT32 timeout) +{ + TQT_DBusError dbuserr; + TQString name = app->adapters[path]->getAlias(dbuserr); + if (dbuserr.isValid()) + tqDebug("Error: %s", dbuserr.message().local8Bit().data()); + slotUpdateToolTip(path, name); +} + +void TrayIcon::slotDiscoverableChanged(const TQString &path, bool state) +{ + TQT_DBusError dbuserr; + TQString name = app->adapters[path]->getAlias(dbuserr); + if (dbuserr.isValid()) + tqDebug("Error: %s", dbuserr.message().local8Bit().data()); + slotUpdateToolTip(path, name); +} + +void TrayIcon::slotUpdateToolTip(const TQString& adapter, const TQString& name) +{ + TQT_DBusError dbuserr; + TQString addr = app->adapters[adapter]->getAddress(dbuserr); + if (dbuserr.isValid()) + tqDebug("Error: %s", dbuserr.message().local8Bit().data()); + TQString disc = + (app->adapters[adapter]->getDiscoverable(dbuserr)) ? "discoverable" : "hidden"; + if (dbuserr.isValid()) + tqDebug("Error: %s", dbuserr.message().local8Bit().data()); + + TQString newText = tr("Adapter: %1\nAddress: %2\nMode: %3\n").arg(name, addr, disc); + updateTooltip(newText); +} + +void TrayIcon::updateTooltip(const TQString &text) +{ + TQString oldText = TQToolTip::textFor(this); + if (oldText != text) + { + TQToolTip::remove(this); + TQToolTip::add(this, text); + } +} + +#include "trayicon.moc" diff --git a/src/tdebluez/trayicon.h b/src/tdebluez/trayicon.h new file mode 100644 index 0000000..b77b918 --- /dev/null +++ b/src/tdebluez/trayicon.h @@ -0,0 +1,160 @@ +/* + * + * TrayIcon for tdebluez + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of tdebluez. + * + * tdebluez is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * tdebluez is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef TRAYICON_H +#define TRAYICON_H + + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "adapterconfigdialog.h" +#include "devicewizard.h" + +class TQTimer; +class TDEToggleAction; +class TDEAction; +class TDEActionMenu; +class TDEPopupMenu; +class KHelpMenu; +class TDEBluetoothApp; + +/** + @author Fred Schaettgen + */ +class TrayIcon: public KSystemTray +{ + Q_OBJECT + +public: + TrayIcon(TDEBluetoothApp* app); + ~TrayIcon(); + // void setAlwaysShowIcon(bool state); + +private slots: + // void slotQuitSelected(); + // void slotShowIconToggled(bool); + void slotServiceConfig(); + // void slotPairedConfig(); + void slotDeviceConfig(); + void slotAdapterConfig(); + void slotInputConfig(); + void slotkbluemonitor(); + void slotkbluelock(); + // void slotkbtobexclient(); + void slotConfigureNotifications() + { + KNotifyDialog::configure(this); + } + ; + void slotQuitSelected(); + void slotReportBug() + { + helpMenu->reportBug(); + } + ; + void slotAbout() + { + helpMenu->aboutApplication(); + } + ; + void slotShowHelp(); +// void slotSearchIcon(); + void slotIconAutostartToggled(bool state); + void slotIconAgentToggled(bool state); + void slotIconObexToggled(bool state); + // gui -> manager + void slotPowerOn(bool state); + // manager -> gui + void slotPowerOnChanged(const TQString&, bool); + void slotDeviceConnectedChanged(const TQString&, bool); + void slotAddAdapter(const TQString&); + void slotRemoveAdapter(const TQString&); + void slotDiscoverableTimeoutChanged(const TQString&, TQ_UINT32 timeout); + void slotDiscoverableChanged(const TQString&, bool state); + // void slotDiscoveringChanged(const TQString&, bool state); + void slotUpdateToolTip(const TQString& adapter, const TQString& name); + void slotAdapterAliasChanged(const TQString&, const TQString&); + +protected: + void mousePressEvent(TQMouseEvent *e); + +private: +// TQTimer *blinkTimer; + bool iconConnectingBlinkState; + + KHelpMenu *helpMenu; + TDEBluetoothApp* app; + + AdapterConfigDialog *adapterConfigDialog; // for BT Adapters + DeviceWizard *deviceWizard; // for BT Devices + + bool acceptClose; + KPixmap iconIdle; + KPixmap iconConnected; + KPixmap iconNoAdapter; + KPixmap iconOff; + + TDEToggleAction *agentIconAction; + TDEToggleAction *obexIconAction; + // TDEToggleAction *showIconAction; + TDEToggleAction *autostartIconAction; + TQMap showPowerOnActionMap; + + TDEAction *serviceConfigAction; + // TDEAction *pairedConfigAction; + TDEAction *adapterConfigAction; + TDEAction *deviceConfigAction; + // TDEAction *inputConfigAction; + TDEAction *showHelpAction; + TDEAction *kbluemonAction; + TDEAction *kbluelockAction; + TDEAction *kbtobexclientAction; + TDEAction *aboutAction; + TDEActionMenu *configActionMenu; + TDEActionMenu *helpActionMenu; + + void setupTray(bool); + void updateIcon(); + + void updateTooltip(const TQString &text); + + TQString localAndEnglish(const TQCString& s); + +signals: + // void setObexSrv(bool); + void setPowerOn(bool); + +}; + +#endif diff --git a/src/tdebluezauth/CMakeLists.txt b/src/tdebluezauth/CMakeLists.txt new file mode 100644 index 0000000..055779d --- /dev/null +++ b/src/tdebluezauth/CMakeLists.txt @@ -0,0 +1,47 @@ +################################################# +# +# (C) 2018 Emanoil Kotsev +# deloptes (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +project( tdebluezauth ) + +# import required +#tde_import( lib... ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/src/libtdebluez + ${CMAKE_BINARY_DIR}/src/libtdebluez + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +# ${DBUS_INCLUDE_DIRS} + ${DBUS_TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + +##### tdebluezauth (tdeinit) ###################### + +tde_add_executable( tdebluezauth AUTOMOC + SOURCES + authorize.cpp authdialog.ui + pindialog.cpp pindefdialog.ui + authservice.cpp application.cpp main.cpp + LINK + ${DBUS_TQT_LIBRARIES} tdebluez-shared bluezinterfaces-static + DESTINATION ${BIN_INSTALL_DIR} +) + +##### other data ################################ +#tde_install_icons( tdebluezauth ) +install( FILES tdebluezauth.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} ) diff --git a/src/tdebluezauth/application.cpp b/src/tdebluezauth/application.cpp new file mode 100644 index 0000000..3b1dec8 --- /dev/null +++ b/src/tdebluezauth/application.cpp @@ -0,0 +1,156 @@ +/* + * + * Authorization Agent implementation of bluez5 + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of tdebluezauth. + * + * tdebluezauth is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * tdebluezauth is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#include +#include +#include + +#include +#include "application.h" +#include "authservice.h" + +#define DBUS_AUTH_SERVICE "TDEBluezAuth" +#define DBUS_AUTH_SERVICE_NAME "org.trinitydesktop.tdebluez" + +TDEBluezAuth::TDEBluezAuth() : + KUniqueApplication() +{ + m_connection = TQT_DBusConnection::addConnection(TQT_DBusConnection::SystemBus, DBUS_AUTH_SERVICE); + + if (!m_connection.isConnected()) + { + kdError() << "Failed to open connection to system message bus: " << m_connection.lastError().message() << endl; + exit(-1); + } + + // try to get a specific service name + if (!m_connection.requestName(DBUS_AUTH_SERVICE_NAME)) + { + tqWarning("Requesting name %s failed. " + "The object will only be addressable through unique name '%s'", + DBUS_AUTH_SERVICE_NAME, m_connection.uniqueName().local8Bit().data()); + exit(-2); + } + + manager = new TDEBluetooth::ObjectManagerImpl("org.bluez", "/", this, "ObjectManager"); + if (!manager->isConnectedToDBUS()) + { + tqDebug("ObjectManager is not connected to DBus"); + exit(-3); + } + + rootService = new RootNodeService(m_connection); + orgService = new OrgNodeService(m_connection); + tdeNodeService = new TrinityDekstopNodeService(m_connection); + authService = new AuthService(m_connection); + agentManager = 0; + if (!configureAgent()) + { + tqDebug("Failed to configure the auth agent"); + } + disableSessionManagement(); + +// connect to manager signals +// connect(manager, SIGNAL(adapterAdded(const TQString&)), SLOT(slotAdapterAdded(const TQString&))); +// connect(manager, SIGNAL(adapterRemoved(const TQString&)), SLOT(slotAdapterRemoved(const TQString&))); + connect(manager, SIGNAL(adapterPowerOnChanged(const TQString&, bool)), + this, SLOT(slotPowerOnChanged(const TQString&, bool))); +} + +TDEBluezAuth::~TDEBluezAuth() +{ + disconnect(manager, SIGNAL(adapterPowerOnChanged(const TQString&, bool)), + this, SLOT(slotPowerOnChanged(const TQString&, bool))); + // close D-Bus connection + unconfigureAgent(); + + m_connection.closeConnection(DBUS_AUTH_SERVICE); + + delete authService; + delete tdeNodeService; + delete orgService; + delete rootService; + + delete manager; + agentManager = 0; +} + +bool TDEBluezAuth::isConnectedToDBUS() +{ + return m_connection.isConnected(); +} + +bool TDEBluezAuth::configureAgent() +{ + if (!agentManager) + agentManager = manager->getAgentManager(); + + if (manager->isAgentRegistered() && manager->isAgentDefaultAgent()) + { + return true; + } + + if (!manager->isAgentRegistered()) + { + if (!manager->registerAgent()) + { + tqWarning("org.bluez.Agent1 registering FAILED"); + return false; + } + } + if (!manager->isAgentDefaultAgent()) + { + if (!manager->requestDefaultAgent()) + { + tqWarning("org.bluez.Agent1 registering FAILED"); + return false; + } + } + + kdDebug() << "org.bluez.Agent1 registering OK" << endl; + + return true; +} + +bool TDEBluezAuth::unconfigureAgent() +{ + if (manager->isAgentRegistered()) + { + if (manager->unregisterAgent()) + kdDebug() << "Agent unregistered OK" << endl; + else + kdDebug() << "Agent unregistered FAILED" << endl; + } + return true; +} + +void TDEBluezAuth::slotPowerOnChanged(const TQString& adapter, bool state) +{ + if (state) + configureAgent(); + else + unconfigureAgent(); +} + +#include "application.moc" diff --git a/src/tdebluezauth/application.h b/src/tdebluezauth/application.h new file mode 100644 index 0000000..499418a --- /dev/null +++ b/src/tdebluezauth/application.h @@ -0,0 +1,79 @@ +/* + * + * Authorization Agent implementation of bluez5 + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of tdebluezauth. + * + * tdebluezauth is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * tdebluezauth is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#ifndef TDEAUTHAPP_H_ +#define TDEAUTHAPP_H_ + +#include + +#include +#include "authservice.h" + +class TDEBluezAuth: public KUniqueApplication +{ + Q_OBJECT + +public: + TDEBluezAuth(); + virtual ~TDEBluezAuth(); + + /*! + * This function return information about connection status to the DBUS daemon. + * \return boolean with the state of the connection to D-Bus + * \retval true if connected + * \retval false if disconnected + */ + bool isConnectedToDBUS(); + +private: + /*! + * This function initialise the connection to the D-Bus daemon. + * \return boolean with the result of the operation + * \retval true if successful initialised D-Bus connection + * \retval false if unsuccessful + */ + bool configureAgent(); + //! to close the connection to D-Bus + bool unconfigureAgent(); + +private: + TQT_DBusConnection m_connection; + + AgentManager1Proxy* agentManager; + RootNodeService *rootService; + OrgNodeService *orgService; + TrinityDekstopNodeService *tdeNodeService; + AuthService *authService; + +public: + TDEBluetooth::ObjectManagerImpl *manager; + +public slots: + // void slotAdapterAdded(const TQString& adapter); + // void slotAdapterRemoved(const TQString& adapter); + void slotPowerOnChanged(const TQString&, bool); + +}; + +#endif // TDEAUTHAPP_H_ diff --git a/src/tdebluezauth/authdialog.ui b/src/tdebluezauth/authdialog.ui new file mode 100644 index 0000000..8b0efcf --- /dev/null +++ b/src/tdebluezauth/authdialog.ui @@ -0,0 +1,52 @@ + +AuthDialog + + + AuthDialog + + + + 0 + 0 + 425 + 300 + + + + + 1 + 1 + 0 + 0 + + + + + 400 + 300 + + + + AuthenticationDialog + + + + unnamed + + + + messageLabel + + + <font size="+2">Bluetooth Authorization Request</font> +<p><b>%1</b> (device address <b>%2</b>) %3.</p> + +<p> +If you aren't sure about the identity of the other party, then reject +this authorization request.</p> + + + + + + diff --git a/src/tdebluezauth/authorize.cpp b/src/tdebluezauth/authorize.cpp new file mode 100644 index 0000000..26aa813 --- /dev/null +++ b/src/tdebluezauth/authorize.cpp @@ -0,0 +1,52 @@ +/* + * + * Authorization dialog for kbluetooth + * + * Copyright (C) 2006 Daniel Gollub + * + * + * This file is part of kbluetooth. + * + * kbluetooth is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * libkbluetooth is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libkbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "authorize.h" +#include "authdialog.h" + +AuthorizeDialog::AuthorizeDialog(const TQString &addr, const TQString &devName, const TQString &service) : + KDialogBase(NULL, "authrequest", true, "Bluetooth Authorization Request", + (Ok | Cancel), Ok, false, + KGuiItem(i18n("Accept"), "accept"), + KGuiItem(i18n("Reject"), "reject")) +{ + TQString action(i18n("wants to act as Input Device")); + + if (!service.isEmpty()) + action.append(" for " + service); + + authDlg = new AuthDialog(this); + authDlg->messageLabel->setText(authDlg->messageLabel->text().arg(devName).arg(addr).arg(action)); + connect(this, SIGNAL(okClicked()), SLOT(close())); + + setMainWidget(authDlg); +} + +AuthorizeDialog::~AuthorizeDialog() +{ + delete authDlg; +} + +#include "authorize.moc" diff --git a/src/tdebluezauth/authorize.h b/src/tdebluezauth/authorize.h new file mode 100644 index 0000000..ff9e9b3 --- /dev/null +++ b/src/tdebluezauth/authorize.h @@ -0,0 +1,47 @@ +/* + * + * Authorization Agent implementation of bluez5 + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of tdebluezauth. + * + * tdebluezauth is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * tdebluezauth is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef AUTHORIZE_H +#define AUTHORIZE_H + +#include + +#include + +#include "application.h" +#include "authdialog.h" + +class AuthorizeDialog : public KDialogBase +{ + + Q_OBJECT + +public: + AuthorizeDialog(const TQString &addr, const TQString &devName, const TQString &service); + ~AuthorizeDialog(); + AuthDialog *authDlg; +}; + +#endif diff --git a/src/tdebluezauth/authservice.cpp b/src/tdebluezauth/authservice.cpp new file mode 100644 index 0000000..419e3e1 --- /dev/null +++ b/src/tdebluezauth/authservice.cpp @@ -0,0 +1,294 @@ +/* + * + * Authorization Agent implementation of bluez5 + * + * Copyright (C) 2019 Emanoil Kotsev + * + * + * This file is part of tdebluezauth. + * + * tdebluezauth is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * tdebluezauth is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +// TQt includes +#include +#include +#include + +#include "authservice.h" +#include "pindialog.h" +#include "authorize.h" + +#define DBUS_AUTH_SERVICE_PATH "/org/trinitydesktop/tdebluez" + +Agent1InterfaceImpl::Agent1InterfaceImpl(TQT_DBusConnection &conn) : + m_connection(&conn) +{ +} + +Agent1InterfaceImpl::~Agent1InterfaceImpl() +{ +} + + +/*! + * Implement virtual methods + * + */ + +void Agent1InterfaceImpl::handleMethodReply(const TQT_DBusMessage& reply) +{ + m_connection->send(reply); +} + +bool Agent1InterfaceImpl::Release(TQT_DBusError& error) +{ + // do something + return true; +} + +void Agent1InterfaceImpl::RequestPinCodeAsync(int asyncCallId, const TQT_DBusObjectPath& device) +{ + TQT_DBusError error; + TDEBluetooth::DeviceImpl *devImpl = new TDEBluetooth::DeviceImpl("org.bluez", device); + devImpl->setConnection((*m_connection)); + TQString addr = devImpl->getAddress(error); + TQString name = devImpl->getAlias(error); + delete devImpl; + PinDialog *pinDialog = new PinDialog(addr, name); + pinDialog->pinDlg->pinEdit->setText(TQString()); + pinDialog->pinDlg->pinEdit->setEnabled(true); + KDialogBase::centerOnScreen(pinDialog); + pinDialog->setActiveWindow(); + pinDialog->show(); + pinDialog->raise(); + + if (pinDialog->exec() == TQDialog::Accepted) + RequestPinCodeAsyncReply(asyncCallId, pinDialog->pinDlg->pinEdit->text()); + else + RequestPinCodeAsyncError(asyncCallId, TQT_DBusError::stdFailed("Request canceled")); + delete pinDialog; +} + +void Agent1InterfaceImpl::DisplayPinCodeAsync(int asyncCallId, const TQT_DBusObjectPath& device, const TQString& pincode) +{ + TQT_DBusError error; + TDEBluetooth::DeviceImpl *devImpl = new TDEBluetooth::DeviceImpl("org.bluez", device); + devImpl->setConnection((*m_connection)); + TQString addr = devImpl->getAddress(error); + TQString name = devImpl->getAlias(error); + kdDebug() << addr << " " << name << endl; + delete devImpl; + PinDialog *pinDialog = new PinDialog(addr, name); + pinDialog->pinDlg->pinEdit->setText(TQString("%1").arg(pincode)); + pinDialog->pinDlg->pinEdit->setEnabled(false); + KDialogBase::centerOnScreen(pinDialog); + pinDialog->setActiveWindow(); + pinDialog->show(); + pinDialog->raise(); + + if (pinDialog->exec() == TQDialog::Accepted) + DisplayPinCodeAsyncReply(asyncCallId); + else + DisplayPinCodeAsyncError(asyncCallId, TQT_DBusError::stdFailed("Request not accepted")); + delete pinDialog; +} + +void Agent1InterfaceImpl::RequestPasskeyAsync(int asyncCallId, const TQT_DBusObjectPath& device) +{ + TQT_DBusError error; + TDEBluetooth::DeviceImpl *devImpl = new TDEBluetooth::DeviceImpl("org.bluez", device); + devImpl->setConnection((*m_connection)); + TQString addr = devImpl->getAddress(error); + TQString name = devImpl->getAlias(error); + delete devImpl; + PinDialog *pinDialog = new PinDialog(addr, name); + pinDialog->pinDlg->pinEdit->setText(TQString()); + pinDialog->pinDlg->pinEdit->setEnabled(true); + KDialogBase::centerOnScreen(pinDialog); + pinDialog->setActiveWindow(); + pinDialog->show(); + pinDialog->raise(); + + if (pinDialog->exec() == TQDialog::Accepted) + RequestPasskeyAsyncReply(asyncCallId, pinDialog->pinDlg->pinEdit->text().toUInt()); + else + RequestPasskeyAsyncError(asyncCallId, TQT_DBusError::stdFailed("Request not accepted")); + delete pinDialog; +} + +void Agent1InterfaceImpl::DisplayPasskeyAsync(int asyncCallId, const TQT_DBusObjectPath& device, TQ_UINT32 passkey, TQ_UINT16 entered) +{ + TQT_DBusError error; + TDEBluetooth::DeviceImpl *devImpl = new TDEBluetooth::DeviceImpl("org.bluez", device); + devImpl->setConnection((*m_connection)); + TQString addr = devImpl->getAddress(error); + TQString name = devImpl->getAlias(error); + delete devImpl; + PinDialog *pinDialog = new PinDialog(addr, name); + pinDialog->pinDlg->pinEdit->setText(TQString("%1").arg(passkey)); + pinDialog->pinDlg->pinEdit->setEnabled(false); + KDialogBase::centerOnScreen(pinDialog); + pinDialog->setActiveWindow(); + pinDialog->show(); + pinDialog->raise(); + + if (pinDialog->exec() == TQDialog::Accepted) + DisplayPasskeyAsyncReply(asyncCallId); + else + DisplayPasskeyAsyncError(asyncCallId, TQT_DBusError::stdFailed("Request not accepted")); + delete pinDialog; +} + +void Agent1InterfaceImpl::RequestConfirmationAsync(int asyncCallId, const TQT_DBusObjectPath& device, TQ_UINT32 passkey) +{ + TQT_DBusError error; + TDEBluetooth::DeviceImpl *devImpl = new TDEBluetooth::DeviceImpl("org.bluez", device); + devImpl->setConnection((*m_connection)); + TQString addr = devImpl->getAddress(error); + TQString name = devImpl->getAlias(error); + delete devImpl; + PinDialog *pinDialog = new PinDialog(addr, name); + pinDialog->pinDlg->pinEdit->setText(TQString("%3").arg(passkey)); + pinDialog->pinDlg->pinEdit->setEnabled(false); + KDialogBase::centerOnScreen(pinDialog); + pinDialog->setActiveWindow(); + pinDialog->show(); + pinDialog->raise(); + + if (pinDialog->exec() == TQDialog::Accepted) + RequestConfirmationAsyncReply(asyncCallId); + else + RequestConfirmationAsyncError(asyncCallId, TQT_DBusError::stdFailed("Request not accepted")); + delete pinDialog; +} + +void Agent1InterfaceImpl::RequestAuthorizationAsync(int asyncCallId, const TQT_DBusObjectPath& device) +{ + TQT_DBusError error; + TDEBluetooth::DeviceImpl *devImpl = new TDEBluetooth::DeviceImpl("org.bluez", device); + devImpl->setConnection((*m_connection)); + TQString addr = devImpl->getAddress(error); + TQString name = devImpl->getAlias(error); + delete devImpl; + AuthorizeDialog *authDialog = new AuthorizeDialog(addr, name, TQString()); + KDialogBase::centerOnScreen(authDialog); + authDialog->setActiveWindow(); + authDialog->show(); + authDialog->raise(); + + if (authDialog->exec() == TQDialog::Accepted) + RequestAuthorizationAsyncReply(asyncCallId); + else + RequestAuthorizationAsyncError(asyncCallId, TQT_DBusError::stdFailed("Request not authorized")); + delete authDialog; +} + +void Agent1InterfaceImpl::AuthorizeServiceAsync(int asyncCallId, const TQT_DBusObjectPath& device, const TQString& uuid) +{ + kdDebug() << __func__ << "()" << endl; + + TQT_DBusError error; + TDEBluetooth::DeviceImpl *devImpl = new TDEBluetooth::DeviceImpl("org.bluez", device); + devImpl->setConnection((*m_connection)); + TQString addr = devImpl->getAddress(error); + TQString name = devImpl->getAlias(error); + delete devImpl; + AuthorizeDialog *authDialog = new AuthorizeDialog(addr, name, resolveUUID(uuid)); + KDialogBase::centerOnScreen(authDialog); + authDialog->setActiveWindow(); + authDialog->show(); + authDialog->raise(); + + if (authDialog->exec() == TQDialog::Accepted) + AuthorizeServiceAsyncReply(asyncCallId); + else + AuthorizeServiceAsyncError(asyncCallId, TQT_DBusError::stdFailed("Request not accepted")); + delete authDialog; +} + +bool Agent1InterfaceImpl::Cancel(TQT_DBusError& error) +{ + kdDebug() << __func__ << "()" << endl; + + // do something + return true; +} + +RootNodeService::RootNodeService(TQT_DBusConnection &connection) : + DBusBaseNode(), m_connection(connection) +{ + addChildNode("org"); + registerObject(m_connection, "/"); +} + +RootNodeService::~RootNodeService() +{ +} + +TQT_DBusObjectBase* RootNodeService::createInterface(const TQString& interfaceName) +{ + return (TQT_DBusObjectBase*) m_interfaces[interfaceName]; +} + +OrgNodeService::OrgNodeService(TQT_DBusConnection &connection) : + DBusBaseNode(), m_connection(connection) +{ + addChildNode("trinitydesktop"); + registerObject(m_connection, "/org"); +} + +OrgNodeService::~OrgNodeService() +{ +} + +TQT_DBusObjectBase* OrgNodeService::createInterface(const TQString& interfaceName) +{ + return (TQT_DBusObjectBase*) m_interfaces[interfaceName]; +} + +TrinityDekstopNodeService::TrinityDekstopNodeService(TQT_DBusConnection &connection) : + DBusBaseNode(), m_connection(connection) +{ + addChildNode("tdebluez"); + registerObject(m_connection, "/org/trinitydesktop"); +} + +TrinityDekstopNodeService::~TrinityDekstopNodeService() +{ +} + +TQT_DBusObjectBase* TrinityDekstopNodeService::createInterface(const TQString& interfaceName) +{ + return (TQT_DBusObjectBase*) m_interfaces[interfaceName]; +} + +AuthService::AuthService(TQT_DBusConnection &conn) : + org::trinitydesktop::tdebluezNode(), m_connection(conn) +{ + m_interfaces.insert("org.freedesktop.DBus.Introspectable", this); + m_interfaces.insert("org.bluez.Agent1", new Agent1InterfaceImpl(m_connection)); + registerObject(m_connection, DBUS_AUTH_SERVICE_PATH); +} + +AuthService::~AuthService() +{ +} + +TQT_DBusObjectBase* AuthService::createInterface(const TQString& interfaceName) +{ + return (TQT_DBusObjectBase*) m_interfaces[interfaceName]; +} diff --git a/src/tdebluezauth/authservice.h b/src/tdebluezauth/authservice.h new file mode 100644 index 0000000..187d4a0 --- /dev/null +++ b/src/tdebluezauth/authservice.h @@ -0,0 +1,230 @@ +/* + * + * Authorization Agent implementation of bluez5 + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of tdebluezauth. + * + * tdebluezauth is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * tdebluezauth is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#ifndef AGENTIMPL_H +#define AGENTIMPL_H + +#include + +#include +#include +#include + +class Agent1InterfaceImpl : public org::bluez::Agent1Interface +{ + +public: + Agent1InterfaceImpl(TQT_DBusConnection&); + virtual ~Agent1InterfaceImpl(); +protected: + /** + * void Release() + * This method gets called when the service daemon + * unregisters the agent. An agent can use it to do + * cleanup tasks. There is no need to unregister the + * agent, because when this method gets called it has + * already been unregistered. + */ + virtual bool Release(TQT_DBusError& error); + /** + * string RequestPinCode(object device) + * + * This method gets called when the service daemon + * needs to get the passkey for an authentication. + * + * The return value should be a string of 1-16 characters + * length. The string can be alphanumeric. + * + * Possible errors: org.bluez.Error.Rejected + * org.bluez.Error.Canceled + */ + virtual void RequestPinCodeAsync(int asyncCallId, const TQT_DBusObjectPath& device); + /** + * void DisplayPinCode(object device, string pincode) + + This method gets called when the service daemon + needs to display a pincode for an authentication. + + An empty reply should be returned. When the pincode + needs no longer to be displayed, the Cancel method + of the agent will be called. + + This is used during the pairing process of keyboards + that don't support Bluetooth 2.1 Secure Simple Pairing, + in contrast to DisplayPasskey which is used for those + that do. + + This method will only ever be called once since + older keyboards do not support typing notification. + + Note that the PIN will always be a 6-digit number, + zero-padded to 6 digits. This is for harmony with + the later specification. + + Possible errors: org.bluez.Error.Rejected + org.bluez.Error.Canceled + */ + virtual void DisplayPinCodeAsync(int asyncCallId, const TQT_DBusObjectPath& device, const TQString& pincode); + /** + * uint32 RequestPasskey(object device) + + This method gets called when the service daemon + needs to get the passkey for an authentication. + + The return value should be a numeric value + between 0-999999. + + Possible errors: org.bluez.Error.Rejected + org.bluez.Error.Canceled + */ + virtual void RequestPasskeyAsync(int asyncCallId, const TQT_DBusObjectPath& device); + /** + * void DisplayPasskey(object device, uint32 passkey, + uint16 entered) + + This method gets called when the service daemon + needs to display a passkey for an authentication. + + The entered parameter indicates the number of already + typed keys on the remote side. + + An empty reply should be returned. When the passkey + needs no longer to be displayed, the Cancel method + of the agent will be called. + + During the pairing process this method might be + called multiple times to update the entered value. + + Note that the passkey will always be a 6-digit number, + so the display should be zero-padded at the start if + the value contains less than 6 digits. + */ + virtual void DisplayPasskeyAsync(int asyncCallId, const TQT_DBusObjectPath& device, TQ_UINT32 passkey, TQ_UINT16 entered); + /** + * void RequestConfirmation(object device, uint32 passkey) + + This method gets called when the service daemon + needs to confirm a passkey for an authentication. + + To confirm the value it should return an empty reply + or an error in case the passkey is invalid. + + Note that the passkey will always be a 6-digit number, + so the display should be zero-padded at the start if + the value contains less than 6 digits. + + Possible errors: org.bluez.Error.Rejected + org.bluez.Error.Canceled + */ + virtual void RequestConfirmationAsync(int asyncCallId, const TQT_DBusObjectPath& device, TQ_UINT32 passkey); + /** + * void RequestAuthorization(object device) + + This method gets called to request the user to + authorize an incoming pairing attempt which + would in other circumstances trigger the just-works + model, or when the user plugged in a device that + implements cable pairing. In the latter case, the + device would not be connected to the adapter via + Bluetooth yet. + + Possible errors: org.bluez.Error.Rejected + org.bluez.Error.Canceled + */ + virtual void RequestAuthorizationAsync(int asyncCallId, const TQT_DBusObjectPath& device); + /** + * void AuthorizeService(object device, string uuid) + + This method gets called when the service daemon + needs to authorize a connection/service request. + + Possible errors: org.bluez.Error.Rejected + org.bluez.Error.Canceled + */ + virtual void AuthorizeServiceAsync(int asyncCallId, const TQT_DBusObjectPath& device, const TQString& uuid); + /** + * void Cancel() + + This method gets called to indicate that the agent + request failed before a reply was returned. + */ + virtual bool Cancel(TQT_DBusError& error); + + virtual void handleMethodReply(const TQT_DBusMessage& reply); + +private: + TQT_DBusConnection *m_connection; +}; + +class AuthService: public org::trinitydesktop::tdebluezNode +{ +public: + AuthService(TQT_DBusConnection&); + ~AuthService(); + +protected: + virtual TQT_DBusObjectBase* createInterface(const TQString&); + +private: + TQMap m_interfaces; + TQT_DBusConnection m_connection; +}; + +class RootNodeService: public DBusBaseNode +{ +public: + RootNodeService(TQT_DBusConnection&); + ~RootNodeService(); +protected: + virtual TQT_DBusObjectBase* createInterface(const TQString&); +private: + TQMap m_interfaces; + TQT_DBusConnection m_connection; +}; + +class OrgNodeService: public DBusBaseNode +{ +public: + OrgNodeService(TQT_DBusConnection&); + ~OrgNodeService(); +protected: + virtual TQT_DBusObjectBase* createInterface(const TQString&); +private: + TQMap m_interfaces; + TQT_DBusConnection m_connection; +}; + +class TrinityDekstopNodeService: public DBusBaseNode +{ +public: + TrinityDekstopNodeService(TQT_DBusConnection&); + ~TrinityDekstopNodeService(); +protected: + virtual TQT_DBusObjectBase* createInterface(const TQString&); +private: + TQMap m_interfaces; + TQT_DBusConnection m_connection; +}; + +#endif // AGENTIMPL_H diff --git a/src/tdebluezauth/main.cpp b/src/tdebluezauth/main.cpp new file mode 100644 index 0000000..98ca19e --- /dev/null +++ b/src/tdebluezauth/main.cpp @@ -0,0 +1,77 @@ +/* + * + * Adapter config dialog for tdebluez authentication + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of tdebluezauth. + * + * tdebluezauth is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * tdebluezauth is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include +#include +#include +#include +#include + +#include "application.h" + +static const char *description = I18N_NOOP("TDEBluezAuth"); +static const char *copy = I18N_NOOP("Copyright (C) 2018 Emanoil."); + +static TDECmdLineOptions options[] = +{ + { 0, 0, 0 } +}; + +int main(int argc, char *argv[]) +{ + TDELocale::setMainCatalogue("tdebluetooth"); + TDEAboutData aboutData("tdebluezauth", + I18N_NOOP("TDEBluezAuth"), + 0, + description, TDEAboutData::License_GPL, + copy,0, "http://trinitydesktop.org"); + aboutData.addAuthor("Tom Patzig", I18N_NOOP("Author"), "tpatzig@suse.de"); + aboutData.addAuthor("Emanoil Kotsev", I18N_NOOP("Bluez5 and port to TDE"), "deloptes@gmail.com"); + TDECmdLineArgs::init( argc, argv, &aboutData ); + TDECmdLineArgs::addCmdLineOptions( options ); + KUniqueApplication::addCmdLineOptions(); + + if (!KUniqueApplication::start()) + { + std::cerr << i18n("TDEBluezAuth is already running.\n").local8Bit(); + return 0; + } + + TDEBluezAuth a; + + if (!a.isConnectedToDBUS()) + { + KMessageBox::error(NULL,i18n("Can't connect to DBus!\nUnable to start tdebluezauth. \n\n \ + Restart dbus and the bluetooth service")); + KUniqueApplication::kApplication()->quit(); + return -1; + } + else + { + return a.exec(); + } + +} diff --git a/src/tdebluezauth/pindefdialog.ui b/src/tdebluezauth/pindefdialog.ui new file mode 100644 index 0000000..4ebc8d4 --- /dev/null +++ b/src/tdebluezauth/pindefdialog.ui @@ -0,0 +1,87 @@ + +PinDefaultDialog + + + PinDefaultDialog + + + + 0 + 0 + 738 + 300 + + + + + 1 + 1 + 0 + 0 + + + + + 400 + 300 + + + + PinDefaultDialog + + + + unnamed + + + + messageLabel + + + <font size="+2">Bluetooth Pairing Request</font> +<p><b>%1</b> (device address <b>%2</b>) wants to pair with your +Bluetooth device, which is needed for authenticated and secure connections. +</p><p> +If you are sure about the identity of the other party, then please enter +the same PIN below as was used by the other device.</p> + + + + + layout3 + + + + unnamed + + + + pinEdit + + + true + + + + + spacer2 + + + Horizontal + + + Expanding + + + + 197 + 20 + + + + + + + + + diff --git a/src/tdebluezauth/pindefdialog2.ui b/src/tdebluezauth/pindefdialog2.ui new file mode 100644 index 0000000..4ebc8d4 --- /dev/null +++ b/src/tdebluezauth/pindefdialog2.ui @@ -0,0 +1,87 @@ + +PinDefaultDialog + + + PinDefaultDialog + + + + 0 + 0 + 738 + 300 + + + + + 1 + 1 + 0 + 0 + + + + + 400 + 300 + + + + PinDefaultDialog + + + + unnamed + + + + messageLabel + + + <font size="+2">Bluetooth Pairing Request</font> +<p><b>%1</b> (device address <b>%2</b>) wants to pair with your +Bluetooth device, which is needed for authenticated and secure connections. +</p><p> +If you are sure about the identity of the other party, then please enter +the same PIN below as was used by the other device.</p> + + + + + layout3 + + + + unnamed + + + + pinEdit + + + true + + + + + spacer2 + + + Horizontal + + + Expanding + + + + 197 + 20 + + + + + + + + + diff --git a/src/tdebluezauth/pindialog.cpp b/src/tdebluezauth/pindialog.cpp new file mode 100644 index 0000000..7f31b89 --- /dev/null +++ b/src/tdebluezauth/pindialog.cpp @@ -0,0 +1,58 @@ +/* + * + * PIN Dialog dialog for tdebluez authentication + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of tdebluezauth. + * + * tdebluezauth is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * tdebluezauth is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "pindialog.h" +#include "pindefdialog.h" + +PinDialog::PinDialog(const TQString &addr, const TQString &devName) : + KDialogBase(NULL, "pinrequest", true, "Pin Request", (Ok | Cancel)) +{ + + pinDlg = new PinDefaultDialog(this); + pinDlg->messageLabel->setText(pinDlg->messageLabel->text().arg(devName).arg(addr)); + + pinDlg->pinEdit->setFocus(); + + connect(this, TQT_SIGNAL(okClicked()), TQT_SLOT(setPin())); + + setMainWidget(pinDlg); +} + +PinDialog::~PinDialog() +{ + delete pinDlg; +} + +void PinDialog::setPin() +{ + pin = pinDlg->pinEdit->text(); +} + +const TQString PinDialog::getPin() +{ + return pin; +} + +#include "pindialog.moc" diff --git a/src/tdebluezauth/pindialog.h b/src/tdebluezauth/pindialog.h new file mode 100644 index 0000000..f99fd1f --- /dev/null +++ b/src/tdebluezauth/pindialog.h @@ -0,0 +1,58 @@ +/* + * + * PIN Dialog dialog for tdebluez authentication + * + * Copyright (C) 2018 Emanoil Kotsev + * + * + * This file is part of tdebluezauth. + * + * tdebluezauth is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * tdebluezauth is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kbluetooth; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef PINDIALOG_H +#define PINDIALOG_H + +#include +#include +#include +#include "application.h" +#include "pindefdialog.h" + +/** +@author Fred Schaettgen + */ + +class PinDialog : public KDialogBase +{ + + Q_OBJECT + +public: + PinDialog(const TQString &addr, const TQString &devName); + ~PinDialog(); + PinDefaultDialog *pinDlg; + const TQString getPin(); + +private: + TQString pin; + +private slots: + void setPin(); + +}; + +#endif diff --git a/src/tdebluezauth/tdebluezauth.desktop b/src/tdebluezauth/tdebluezauth.desktop new file mode 100644 index 0000000..efdeaf4 --- /dev/null +++ b/src/tdebluezauth/tdebluezauth.desktop @@ -0,0 +1,36 @@ +[Desktop Entry] +Encoding=UTF-8 +Exec=tdebluezauth +Icon=tdebluez +Type=Application +Name=tdebluezauth +GenericName=Bluetooth Wizard +GenericName[de]=Bluetooth-Assistent +GenericName[es]=Asistente de bluetooth +GenericName[et]=Bluetoothi nõustaja +GenericName[it]=Assistente per Bluetooth +GenericName[ja]=Bluetooth ウィザード +GenericName[nl]=Bluetooth-assistent +GenericName[pt]=Assistente de Bluetooth +GenericName[pt_BR]=Assistente de Bluetooth +GenericName[sr]=Bluetooth чаробњак +GenericName[sr@Latn]=Bluetooth čarobnjak +GenericName[sv]=Blåtandsguide +GenericName[tg]=Устоди Bluetooth +GenericName[tr]=Bluetooth Sihirbazı +GenericName[xx]=xxBluetooth Wizardxx +Categories=Qt;TDE;System;Monitor; +Comment=TDE Bluetooth Wizard +Comment[de]=TDE Bluetooth-Assistent +Comment[es]=Asistente de bluetooth para TDE +Comment[et]=TDE Bluetoothi nõustaja +Comment[it]=Assistente per Bluetooth di TDE +Comment[ja]=TDE Bluetooth ウィザード +Comment[nl]=TDE Bluetooth-assistent +Comment[pt]=Assistente de Bluetooth do TDE +Comment[pt_BR]=Assistente de Bluetooth do TDE +Comment[sr]=TDE чаробњак за Bluetooth +Comment[sr@Latn]=TDE čarobnjak za Bluetooth +Comment[sv]=TDE:s Blåtandsguide +Comment[tg]=Устоди TDE Bluetooth +Comment[tr]=TDE Bluetooth Sihirbazı diff --git a/src/tdeioclient/CMakeLists.txt b/src/tdeioclient/CMakeLists.txt new file mode 100644 index 0000000..19cc86d --- /dev/null +++ b/src/tdeioclient/CMakeLists.txt @@ -0,0 +1,40 @@ +################################################# +# +# (C) 2018 Emanoil Kotsev +# deloptes (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +project (tdeioclient) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/src/libtdebluez + ${CMAKE_SOURCE_DIR}/src/tdeioclient + ${CMAKE_BINARY_DIR}/src + ${CMAKE_BINARY_DIR}/src/tdeioclient + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} + ${DBUS_INCLUDE_DIRS} + ${DBUS_TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + +##### tdeioclient (tdeinit) ###################### + +tde_add_executable ( tdeioclient AUTOMOC + SOURCES + commandhandler.cpp main.cpp + LINK + ${DBUS_TQT_LIBRARIES} tdecore-shared tdebluez-shared tdeio-shared + DESTINATION ${BIN_INSTALL_DIR} +) diff --git a/src/tdeioclient/commandhandler.cpp b/src/tdeioclient/commandhandler.cpp new file mode 100644 index 0000000..4a24716 --- /dev/null +++ b/src/tdeioclient/commandhandler.cpp @@ -0,0 +1,407 @@ +/*************************************************************************** + * Copyright (C) 2004 by Fred Schaettgen * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ +#include "commandhandler.h" +#include +#include +#include +#include +#include +#include +#include +#include + +CommandHandler::CommandHandler(TDECmdLineArgs *a) +{ + this->iterate = false; + this->args = a; + returnValue = 0; + if (args->count() < 2) + { + exitPrintUsage(i18n("Arguments missing.")); + } + origCommand = args->arg(0); + for (int n = 1; n < args->count() - 1; ++n) + { + sources.append(args->arg(n)); + } + target = args->arg(args->count() - 1); + targets = sources; + targets.append(target); + + if (args->isSet("outfile")) + { + outFile.setName(args->getOption("outfile")); + outFile.open(IO_WriteOnly); + } + else + { + // Open stdout + outFile.open(IO_WriteOnly, 1); + } + + if (args->isSet("infile")) + { + inFile.setName(args->getOption("infile")); + inFile.open(IO_ReadOnly); + } + else + { + // Open stdin + inFile.open(IO_ReadOnly, 0); + } + + showProgressWindow = args->isSet("progresswindow"); + overwrite = args->isSet("overwrite"); +} + +CommandHandler::~CommandHandler() +{ +} + +void CommandHandler::start() +{ + if (origCommand == "ls") + { + command = "ls"; + list(target); + } + else if (origCommand == "get" || origCommand == "cat" || origCommand == "read") + { + command = "get"; + get(target); + } + else if (origCommand == "put" || origCommand == "write") + { + command = "put"; + put(target); + } + else if (origCommand == "mkdir") + { + command = "mkdir"; + mkdir(target); + } + else if (origCommand == "rmdir") + { + command = "rmdir"; + rmdir(target); + } + else if (origCommand == "rm" || origCommand == "del") + { + command = "del"; + del(targets); + } + else if (origCommand == "cp" || origCommand == "copy") + { + command = "copy"; + copy(sources, target); + } + else + { + command = origCommand; + exitPrintUsage(i18n("unknown command: %1").arg(command)); + } +} + +void CommandHandler::commonConnect(TDEIO::Job* job) +{ + connect(job, SIGNAL(infoMessage(TDEIO::Job*,const TQString&)), + this, SLOT(slotInfoMessage(TDEIO::Job*,const TQString&))); + connect(job, SIGNAL(percent (TDEIO::Job*, unsigned long)), + this, SLOT(slotPercent(TDEIO::Job*, unsigned long))); + connect(job, SIGNAL(result(TDEIO::Job*)), this, SLOT(slotFinished(TDEIO::Job*))); +} + +KURL::List CommandHandler::urlList(const TQStringList& sources) +{ + KURL::List ret; + for (size_t n = 0; n < sources.size(); ++n) + { + ret.append(KURL(sources[n])); + } + return ret; +} + +// Commands --------------------------------------------------- + +void CommandHandler::list(const TQString& target) +{ + bool showHidden = true; + TDEIO::ListJob* job = TDEIO::listDir(KURL(target), showProgressWindow, showHidden); + this->job = job; + commonConnect(job); + connect(job, SIGNAL(entries(TDEIO::Job*, const TDEIO::UDSEntryList&)), + this, SLOT(slotEntries(TDEIO::Job*, const TDEIO::UDSEntryList&))); +} + +void CommandHandler::get(const TQString& target) +{ + bool reload = false; + TDEIO::TransferJob* job = TDEIO::get(KURL(target), reload, showProgressWindow); + this->job = job; + commonConnect(job); + connect(job, SIGNAL(data(TDEIO::Job*,const TQByteArray&)), + this, SLOT(slotData(TDEIO::Job*,const TQByteArray&))); +} + +void CommandHandler::put(const TQString& target) +{ + int permissions = -1; + bool resume = false; + TDEIO::TransferJob* job = TDEIO::put(KURL(target), permissions, + overwrite, resume, showProgressWindow); + this->job = job; + commonConnect(job); + connect(job, SIGNAL(dataReq(TDEIO::Job*,TQByteArray&)), + this, SLOT(slotDataReq(TDEIO::Job*,TQByteArray&))); +} + +void CommandHandler::mkdir(const TQString& target) +{ + int permissions = -1; + TDEIO::SimpleJob* job = TDEIO::mkdir(KURL(target), permissions); + this->job = job; + commonConnect(job); +} + +void CommandHandler::rmdir(const TQString& target) +{ + TDEIO::SimpleJob* job = TDEIO::rmdir(KURL(target)); + this->job = job; + commonConnect(job); +} + +void CommandHandler::del(const TQStringList& targets) +{ + bool shred = false; + TDEIO::DeleteJob* job = TDEIO::del(urlList(targets), shred, showProgressWindow); + this->job = job; + commonConnect(job); +} + +void CommandHandler::copy(const TQStringList& sources, const TQString& target) +{ + TDEIO::CopyJob *job = TDEIO::copy(urlList(sources), KURL(target), + showProgressWindow); + this->job = job; + commonConnect(job); +} + +// Signal handlers -------------------------------------------- + +void CommandHandler::slotEntries(TDEIO::Job* /*job*/, const TDEIO::UDSEntryList& list) +{ + if (command == "ls") + { + for (size_t n = 0; n < list.size(); ++n) + { + TDEIO::UDSEntry entry = list[n]; + + TQDateTime date = TQDateTime::currentDateTime(); + TQString user = "n/a"; + TQString iconName = "unknown"; + TQString group = "n/a"; + TQString extra = "n/a"; + TQString name = "n/a"; + TQDateTime mTime = TQDateTime::currentDateTime(); + TQDateTime aTime = TQDateTime::currentDateTime(); + TQDateTime cTime = TQDateTime::currentDateTime(); + int fileType = 0; + TQString linkDest = "n/a"; + TQString url = "n/a"; + TQString mimeType = "n/a"; + TQString guessedMimeType = "n/a"; + TQString xmlProperties = "n/a"; + long long size = -1; + int access = 0; + + for (size_t m = 0; m < entry.size(); ++m) + { + TDEIO::UDSAtom atom = entry[m]; + switch (atom.m_uds) + { + case TDEIO::UDS_TIME: + date.setTime_t(atom.m_long); + break; + case TDEIO::UDS_SIZE: + size = atom.m_long; + break; + case TDEIO::UDS_USER: + user = atom.m_str; + break; + case TDEIO::UDS_ICON_NAME: + iconName = atom.m_str; + break; + case TDEIO::UDS_GROUP: + group = atom.m_str; + break; + case TDEIO::UDS_EXTRA: + extra = atom.m_str; + break; + case TDEIO::UDS_NAME: + name = atom.m_str; + break; + case TDEIO::UDS_ACCESS: + access = atom.m_long; + break; + case TDEIO::UDS_MODIFICATION_TIME: + mTime.setTime_t(atom.m_long); + break; + case TDEIO::UDS_ACCESS_TIME: + aTime.setTime_t(atom.m_long); + break; + case TDEIO::UDS_CREATION_TIME: + cTime.setTime_t(atom.m_long); + break; + case TDEIO::UDS_FILE_TYPE: + fileType = atom.m_long; + break; + case TDEIO::UDS_LINK_DEST: + linkDest = atom.m_str; + break; + case TDEIO::UDS_URL: + url = atom.m_str; + break; + case TDEIO::UDS_MIME_TYPE: + mimeType = atom.m_str; + break; + case TDEIO::UDS_GUESSED_MIME_TYPE: + guessedMimeType = atom.m_str; + break; + case TDEIO::UDS_XML_PROPERTIES: + xmlProperties = atom.m_str; + break; + }; + } + + if (args->isSet("access")) + { + std::cout << TQString::number(access, 8).local8Bit() << " "; + } + if (args->isSet("filetype")) + { + std::cout << TQString::number(fileType).rightJustify(4).local8Bit() << " "; + } + if (args->isSet("user")) + { + std::cout << user.rightJustify(8).local8Bit() << " "; + } + if (args->isSet("group")) + { + std::cout << group.rightJustify(8).local8Bit() << " "; + } + if (args->isSet("size")) + { + std::cout << TQString::number(size).rightJustify(9).local8Bit() << " "; + } + if (args->isSet("date")) + { + std::cout << date.toString("yyyy-MM-dd hh:mm:ss").local8Bit() << " "; + } + if (args->isSet("mtime")) + { + std::cout << mTime.toString("yyyy-MM-dd hh:mm:ss").local8Bit() << " "; + } + if (args->isSet("atime")) + { + std::cout << aTime.toString("yyyy-MM-dd hh:mm:ss").local8Bit() << " "; + } + if (args->isSet("ctime")) + { + std::cout << cTime.toString("yyyy-MM-dd hh:mm:ss").local8Bit() << " "; + } + if (args->isSet("iconName")) + { + std::cout << iconName.local8Bit() << " "; + } + if (args->isSet("mimetype")) + { + std::cout << mimeType.local8Bit() << " "; + } + if (args->isSet("guessedmimetype")) + { + std::cout << guessedMimeType.local8Bit() << " "; + } + if (args->isSet("linkdest")) + { + std::cout << linkDest.local8Bit() << " "; + } + if (args->isSet("url")) + { + std::cout << url.local8Bit() << " "; + } + if (args->isSet("name")) + { + std::cout << name.local8Bit() << " "; + } + if (args->isSet("xmlproperties")) + { + std::cout << xmlProperties.local8Bit() << " "; + } + if (args->isSet("extra")) + { + std::cout << extra.local8Bit() << " "; + } + std::cout << std::endl; + } + } +} + +void CommandHandler::slotData(TDEIO::Job* /*job*/, const TQByteArray &data) +{ + outFile.writeBlock(data); +} + +void CommandHandler::slotDataReq(TDEIO::Job* /*job*/, TQByteArray &data) +{ + data.resize(65536); + data.resize(inFile.readBlock(data.data(), data.size())); +} + +void CommandHandler::slotInfoMessage(TDEIO::Job* /*job*/, const TQString& msg) +{ + if (msg != lastMessage && args->isSet("messages")) + { + std::cerr << " >" << msg.local8Bit() << std::endl; + lastMessage = msg; + } +} + +void CommandHandler::slotPercent(TDEIO::Job* /*job*/, unsigned long /*percent*/) +{ + +} + +void CommandHandler::slotFinished(TDEIO::Job* job) +{ + if (job->error() != 0) + { + std::cerr << job->errorString().local8Bit() << std::endl; + iterate = false; + } + else + { + if (iterate) + { + start(); + } + } + + if (!iterate) + { + TDEApplication::kApplication()->exit(job->error()); + } +} + +void CommandHandler::exitPrintUsage(const TQString& message) +{ + std::cout << message.local8Bit() << std::endl; + TDECmdLineArgs::usage(); +} + +#include "commandhandler.moc" diff --git a/src/tdeioclient/commandhandler.h b/src/tdeioclient/commandhandler.h new file mode 100644 index 0000000..29fb552 --- /dev/null +++ b/src/tdeioclient/commandhandler.h @@ -0,0 +1,60 @@ +//-*-c++-*- +/*************************************************************************** + * Copyright (C) 2004 by Fred Schaettgen * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ +#ifndef COMMANDHANDLER_H +#define COMMANDHANDLER_H + +#include +#include +#include +#include +#include + +class TDECmdLineArgs; +namespace TDEIO { class Job; } + +class CommandHandler : public TQObject +{ + Q_OBJECT + +public: + CommandHandler(TDECmdLineArgs *args); + ~CommandHandler(); + void start(); + +private: + KURL::List urlList(const TQStringList& list); + void exitPrintUsage(const TQString& message); + void list(const TQString& target); + void get(const TQString& target); + void put(const TQString& target); + void mkdir(const TQString& target); + void rmdir(const TQString& target); + void del(const TQStringList& targets); + void copy(const TQStringList& sources, const TQString& target); + void commonConnect(TDEIO::Job* job); + + TQString command, origCommand, target, lastMessage; + TQStringList targets, sources; + TDEIO::Job* job; + int returnValue; + TDECmdLineArgs *args; + TQFile outFile, inFile; + bool showProgressWindow, overwrite, iterate; + +private slots: + void slotFinished(TDEIO::Job *job); + void slotEntries(TDEIO::Job* job, const TDEIO::UDSEntryList& list); + void slotData(TDEIO::Job *, const TQByteArray &data); + void slotDataReq(TDEIO::Job *, TQByteArray &data); + void slotInfoMessage(TDEIO::Job* job,const TQString& msg); + void slotPercent(TDEIO::Job* job, unsigned long percent); +}; + +#endif diff --git a/src/tdeioclient/main.cpp b/src/tdeioclient/main.cpp new file mode 100644 index 0000000..59762b7 --- /dev/null +++ b/src/tdeioclient/main.cpp @@ -0,0 +1,73 @@ +//-*-c++-*- +/*************************************************************************** + * Copyright (C) 2003 by Fred Schaettgen * + * kdebluetooth@schaettgen.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "commandhandler.h" + +static const char description[] = + I18N_NOOP("TDEIO command line client for tdebluez"); + +//FIXME static const char* version = TDEBluetoothConfig::version; + +static const char* version = "0.0.1"; + +static TDECmdLineOptions options[] = +{ + {"d", 0, 0}, {"date", I18N_NOOP("List date"), 0}, + {"u", 0, 0}, {"user", I18N_NOOP("List user"), 0}, + {"i", 0, 0}, {"iconName", I18N_NOOP("List icon name"), 0}, + {"g", 0, 0}, {"group", I18N_NOOP("List group"), 0}, + {"extra", I18N_NOOP("List extra"), 0}, + {"noname", I18N_NOOP("Do not list name"), 0}, + {"p", 0, 0}, {"access", I18N_NOOP("List access permissions"), 0}, + {"m", 0, 0}, {"mtime", I18N_NOOP("List modification time"), 0}, + {"a", 0, 0}, {"atime", I18N_NOOP("List access time"), 0}, + {"c", 0, 0}, {"ctime", I18N_NOOP("List creation time"), 0}, + {"f", 0, 0}, {"filetype", I18N_NOOP("List file type"), 0}, + {"D", 0, 0}, {"linkdest", I18N_NOOP("List link destination"), 0}, + {"U", 0, 0}, {"url", I18N_NOOP("List URL"), 0}, + {"M", 0, 0}, {"mimetype", I18N_NOOP("List mime type"), 0}, + {"G", 0, 0}, {"guessedmimetype", I18N_NOOP("List guessed mime type"), 0}, + {"X", 0, 0}, {"xmlproperties", I18N_NOOP("List XML properties"), 0}, + {"s", 0, 0}, {"size", I18N_NOOP("List size"), 0}, + {"outfile [filename]", I18N_NOOP("Output file. Defaults to stdout"), 0}, + {"infile [filename]", I18N_NOOP("Input file. Defaults to stdin"), 0}, + {"progresswindow", I18N_NOOP("Show a progress window"), 0}, + {"nooverwrite", I18N_NOOP("Ask (graphically) before overwriting files"), 0}, + {"messages", I18N_NOOP("Show messages from the tdeioslave"), 0}, + {"+[cmd]", I18N_NOOP("Command (ls, cat, put, cp, rm, mv, mkdir, rmdir)"), 0 }, + TDECmdLineLastOption +}; + +int main(int argc, char **argv) +{ + TDEAboutData about("tdeioclient", + I18N_NOOP("tdeio client"), + version, description, + TDEAboutData::License_GPL, + "(C) 2004 Fred Schaettgen", 0, 0, + "kdebluetooth@schaettgen.de"); + about.addAuthor("Emanoil Kotsev", I18N_NOOP("Bluez5 and port to TDE"), "deloptes@gmail.com"); + TDECmdLineArgs::init(argc, argv, &about); + TDECmdLineArgs::addCmdLineOptions(options); + TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs(); + TDEApplication app; + + CommandHandler commandHandler(args); + commandHandler.start(); + return app.exec(); +} diff --git a/src/tdeioslave/CMakeLists.txt b/src/tdeioslave/CMakeLists.txt new file mode 100644 index 0000000..11b6614 --- /dev/null +++ b/src/tdeioslave/CMakeLists.txt @@ -0,0 +1,13 @@ +################################################# +# +# (C) 2018 Emanoil Kotsev +# deloptes (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( bluetooth ) +add_subdirectory( obex ) diff --git a/src/tdeioslave/bluetooth/CMakeLists.txt b/src/tdeioslave/bluetooth/CMakeLists.txt new file mode 100644 index 0000000..f74af27 --- /dev/null +++ b/src/tdeioslave/bluetooth/CMakeLists.txt @@ -0,0 +1,40 @@ +################################################# +# +# (C) 2018 Emanoil Kotsev +# deloptes (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/src/libtdebluez + ${CMAKE_BINARY_DIR}/src/libtdebluez + ${CMAKE_SOURCE_DIR}/src/tdeioslave/bluetooth + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} + ${DBUS_INCLUDE_DIRS} + ${DBUS_TQT_INCLUDE_DIRS} +) +##### tdeio_bluetooth (static) ############################# +set( target tdeio_bluetooth ) + +tde_add_kpart( ${target} AUTOMOC + SOURCES tdeiobluetooth.cpp + LINK ${DBUS_TQT_LIBRARIES} tdeio-shared tdebluez-shared bluezinterfaces-static + DESTINATION ${PLUGIN_INSTALL_DIR} +) + +# konqueror sidebar entry +install( + FILES bluetooth.protocol + DESTINATION ${SERVICES_INSTALL_DIR} ) +install( + FILES bluetooth_sidebarentry.desktop + DESTINATION ${DATA_INSTALL_DIR}/konqsidebartng/virtual_folders/services +) diff --git a/src/tdeioslave/bluetooth/bluetooth.protocol b/src/tdeioslave/bluetooth/bluetooth.protocol new file mode 100644 index 0000000..b99a1f5 --- /dev/null +++ b/src/tdeioslave/bluetooth/bluetooth.protocol @@ -0,0 +1,33 @@ +[Protocol] +exec=tdeio_bluetooth +protocol=bluetooth +input=none +output=filesystem +reading=true +listing=Name,Type +Icon=tdebluez +Description=Bluetooth inquiry protocol +Description[bg]=Протокол за заявка към Bluetooth +Description[ca]=Protocol de recerca Bluetooth +Description[da]=Bluetooth forespørgselsprotokol +Description[de]=Bluetooth-Anfrageprotokoll +Description[el]=Πρωτόκολλο έρευνας Bluetooth +Description[es]=Protocolo de búsqueda Bluetooth +Description[et]=Bluetoothi päringuprotokoll +Description[fr]=Protocole de Demande de renseignements Bluetooth +Description[gl]=Protocolo de pestquisa Bluetooth +Description[it]=Protocollo di richiesta Bluetooth +Description[ja]=Bluetooth 問い合わせプロトコル +Description[lt]=Bluetooth užklausų protokolas +Description[nl]=Bluetooth inquiry-protocol +Description[pl]=Protokół wyszukiwania Bluetooth +Description[pt]=Protocolo de intquisição Bluetooth +Description[pt_BR]=Protocolo de intquisição Bluetooth +Description[sr]=Протокол испитивања Bluetooth-а +Description[sr@Latn]=Protokol ispitivanja Bluetooth-a +Description[sv]=Blåtand frågeprotokoll +Description[ta]=புளூடூத்தை உள்ளிடும் நெறிமுறை +Description[tr]=Bluetooth sorgulama protokolü +Description[xx]=xxBluetooth inquiry protocolxx +maxInstances=3 +DocPath=tdebluez/components.tdeio_sdp.html diff --git a/src/tdeioslave/bluetooth/bluetooth_sidebarentry.desktop b/src/tdeioslave/bluetooth/bluetooth_sidebarentry.desktop new file mode 100644 index 0000000..79306e0 --- /dev/null +++ b/src/tdeioslave/bluetooth/bluetooth_sidebarentry.desktop @@ -0,0 +1,35 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Link +URL=bluetooth:/ +Icon=tdebluez +Name=Bluetooth Browser +Name[ar]= متصفح Bluetooth +Name[bg]=Браузър за Bluetooth +Name[br]=Furcher Bluetooth +Name[ca]=Fullejador Bluetooth +Name[da]=Bluetooth-browser +Name[de]=Bluetooth-Browser +Name[el]=Περιηγητής Bluetooth +Name[es]=Navegador Bluetooth +Name[et]=Bluetoothi brauser +Name[fr]=Navigateur Bluetooth +Name[ga]=Brabhsálaí Bluetooth +Name[gl]=Navegador Bluetooth +Name[it]=Navigatore Bluetooth +Name[ja]=Bluetooth ブラウザ +Name[lt]=Bluetooth naršyklė +Name[pa]=ਬਲਿਊਟੁੱਥ ਝਲਕਾਰਾ +Name[pl]=Przeglądarka Bluetooth +Name[pt]=Navegador Bluetooth +Name[pt_BR]=Navegador Bluetooth +Name[ru]=Обозреватель Bluetooth +Name[sr]=Bluetooth прегледач +Name[sr@Latn]=Bluetooth pregledač +Name[sv]=Blåtandsbläddrare +Name[ta]=புலுடுத் உலாவி +Name[tr]=Bluetooth Tarayıcısı +Name[xx]=xxBluetooth Browserxx +Open=false +X-TDE-TreeModule=Directory +X-TDE-KonqSidebarModule=konqsidebar_tree diff --git a/src/tdeioslave/bluetooth/tdeiobluetooth.cpp b/src/tdeioslave/bluetooth/tdeiobluetooth.cpp new file mode 100644 index 0000000..d2268a7 --- /dev/null +++ b/src/tdeioslave/bluetooth/tdeiobluetooth.cpp @@ -0,0 +1,464 @@ +//-*-c++-*- +/*************************************************************************** + * Copyright (C) 2003 by Fred Schaettgen * + * kdebluetooth@schaettgen.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#include "tdeiobluetooth.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace TDEBluetooth; + +static const TDECmdLineOptions options[] = +{ + { "+protocol", I18N_NOOP( "Protocol name" ), 0 }, + { "+pool", I18N_NOOP( "Socket name" ), 0 }, + { "+app", I18N_NOOP( "Socket name" ), 0 }, + TDECmdLineLastOption +}; + +extern "C" +{ + int kdemain(int argc, char **argv) { + TDEInstance instance( "tdeio_bluetooth" ); + kdDebug() << "*** Starting tdeio_bluetooth " << endl; + if (argc != 4) { + kdDebug() << "Usage: tdeio_bluetooth protocol domain-socket1 domain-socket2" << endl; + exit(-1); + } + + putenv(strdup("SESSION_MANAGER=")); + TDECmdLineArgs::init(argc, argv, "tdeio_bluetooth", 0, 0, 0, 0); + TDECmdLineArgs::addCmdLineOptions( options ); + + TDEApplication app( false, false, false ); + app.dcopClient()->attach(); + + TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs(); + TDEioBluetooth slave( args->arg(0), args->arg(1), args->arg(2) ); + kdDebug() << "*** protocol " << args->arg(0) << endl; + kdDebug() << "*** pool socket " << args->arg(1) << endl; + kdDebug() << "*** app socket " << args->arg(2) << endl; + slave.dispatchLoop(); + kdDebug() << "*** tdeio_bluetooth Done" << endl; + return 0; + } +} + + +TDEioBluetooth::TDEioBluetooth(const TQCString &protocol, const TQCString &pool_socket, const TQCString &app_socket) : + ForwardingSlaveBase(protocol, pool_socket, app_socket) +{ + kdDebug() << k_funcinfo << endl; + + TDELocale::setMainCatalogue("tdebluez"); + TQT_DBusError error; + adapter = 0; + + manager = new TDEBluetooth::ObjectManagerImpl("org.bluez", "/"/*, this, "ObexObjectManager"*/); + + if (!manager) + { + ForwardingSlaveBase::error(TDEIO::ERR_SERVICE_NOT_AVAILABLE, i18n("Bluetooth Manager not found")); + closeConnection(); + } + else + { + if (!manager->getAdapters().isEmpty()) + { + TDEBluetooth::ObjectManagerImpl::AdapterList al = manager->getAdapters(); + TDEBluetooth::ObjectManagerImpl::AdapterList::Iterator ait = al.begin(); + for (ait; ait != al.end(); ++ait) + { + TDEBluetooth::AdapterImpl *ad = new TDEBluetooth::AdapterImpl("org.bluez", (*ait)); + ad->setConnection((*(manager->getConnection()))); + // FIXME implement multiple adapters + if (ad->getPowered(error)) + { + adapter = ad; + break; + } + } + connect(manager, SIGNAL(deviceAdded(const TQString &)), + this, SLOT(slotAddDevice(const TQString &))); + connect(manager, SIGNAL(deviceRemoved(const TQString &)), + this, SLOT(slotRemoveDevice(const TQString &))); + connect(manager, SIGNAL(adapterPowerOnChanged(const TQString&, bool )), + this, SLOT(slotAdapterPowerOnChanged(const TQString &, bool ))); + } + else + { + ForwardingSlaveBase::error(TDEIO::ERR_SERVICE_NOT_AVAILABLE, i18n("No adapter found")); + closeConnection(); + } + } +} + +TDEioBluetooth::~TDEioBluetooth() +{ + kdDebug() << k_funcinfo << endl; + if (manager) delete manager; + if (adapter) delete adapter; +} + +void TDEioBluetooth::closeConnection() +{ + kdDebug() << k_funcinfo << endl; + exit(); +} + +void TDEioBluetooth::stat(const KURL &url) +{ + kdDebug() << __func__ << "(" << url.prettyURL() << ")" << endl; + + TDEIO::UDSEntry entry; + + if (!adapter) + { +// ForwardingSlaveBase::warning(i18n("Bluetooth Adapter not found")); +// ForwardingSlaveBase::error(TDEIO::ERR_SERVICE_NOT_AVAILABLE, i18n("Bluetooth Adapter not found")); + TQString name = "No device found"; + TQ_UINT32 devClass = 0; + + addAtom(entry, TDEIO::UDS_NAME, name); + addAtom(entry, TDEIO::UDS_FILE_TYPE, S_IFDIR); + addAtom(entry, TDEIO::UDS_ACCESS, 0555); + addAtom(entry, TDEIO::UDS_MIME_TYPE, DeviceMimeConverter::classToMimeType(devClass)); + addAtom(entry, TDEIO::UDS_ICON_NAME, DeviceMimeConverter::classToIconName(devClass)); + + return; + } + + TQT_DBusError dbuserror; + TQString path = url.path(); + if (path.isEmpty() || path == "/") + { + // The root is "virtual" - it's not a single physical directory + createTopLevelEntry(entry); + } + else if (path.find(TQRegExp("/\\[([0-9A-F]{2}:){5}[0-9A-F]{2}\\]"), 0) != -1) + { + createDirEntry(entry, path, path); + } + else + { + ForwardingSlaveBase::error(TDEIO::ERR_MALFORMED_URL, i18n("Could not stat %1.").arg(url.prettyURL())); + } + statEntry(entry); + finished(); +} + +void TDEioBluetooth::listDir(const KURL &url) +{ + kdDebug() << k_funcinfo << endl; + + if (!adapter) + { + ForwardingSlaveBase::error(TDEIO::ERR_SERVICE_NOT_AVAILABLE, i18n("Bluetooth Adapter not found")); + return; + } + + TDEIO::UDSEntry entry; + TQValueList list; + TQT_DBusError error; + TDEIO::UDSEntryList entries; + + TQString path = url.path(); + TQString name = adapter->getName(error); + + kdDebug() << __func__ << "(" << path << ")" << endl; + + TQRegExp rx("/" + name + "/\\[([0-9A-F]{2}:){5}[0-9A-F]{2}\\]"); + kdDebug() << "Regex: " << rx.search(path) << endl; + + if (rx.search(path) == 0) + { + listServices(list, url); + } + else if (path == "/" + name) + { + listDevices(list, url); + } + else if (path == "/") + { + createTopLevelEntry(entry); + listEntry(entry, false); + } + else + { + ForwardingSlaveBase::listDir(url); + } + + if (list.count() > 0) + { + kdDebug() << __func__ << "(" << path << ")" << endl; + totalSize(list.count() + 1); + + TDEIO::UDSEntryListIterator it = list.begin(); + TDEIO::UDSEntryListIterator end = list.end(); + for (; it != end; ++it) + { + entries.append(*it); + } + listEntries(entries); + } + listEntry(entry, true); + + finished(); + return; +} + +bool TDEioBluetooth::rewriteURL(const KURL &url, KURL &newUrl) +{ + kdDebug() << k_funcinfo << endl; + + TQString path = url.path(); + TQString protocol = url.protocol(); + + if (protocol == "obexopp" || protocol == "obexftp") + { + newUrl = url; + return true; + } + else + { + ForwardingSlaveBase::error(TDEIO::ERR_MALFORMED_URL, url.prettyURL()); + return false; + } +} + +void TDEioBluetooth::createTopLevelEntry(TDEIO::UDSEntry &entry) +{ + kdDebug() << k_funcinfo << endl; + + TQT_DBusError error; + TQString name = adapter->getName(error); + TQ_UINT32 devClass = adapter->getClass(error); + + addAtom(entry, TDEIO::UDS_NAME, name); + addAtom(entry, TDEIO::UDS_FILE_TYPE, S_IFDIR); + addAtom(entry, TDEIO::UDS_ACCESS, 0555); + addAtom(entry, TDEIO::UDS_MIME_TYPE, DeviceMimeConverter::classToMimeType(devClass)); + addAtom(entry, TDEIO::UDS_ICON_NAME, DeviceMimeConverter::classToIconName(devClass)); +} + +bool TDEioBluetooth::listDevice(TDEIO::UDSEntry &entry, const TQString &path, const KURL &url) +{ + kdDebug() << __func__ << "(" << url << ")" << endl; + TQT_DBusError error; + + TDEBluetooth::DeviceImpl *dev = new TDEBluetooth::DeviceImpl("org.bluez", path); + dev->setConnection((*(manager->getConnection()))); + + const TQString addr = dev->getAddress(error); + TQString name = dev->getName(error); + TQString alias = dev->getAlias(error); + const int devClass = dev->getClass(error); + TQString aname = adapter->getName(error); + + delete dev; + + entry.clear(); + + if (!alias.isEmpty()) + name = alias; + else + name = alias = addr; + + createDirEntry(entry, name, TQString("bluetooth:/%1/[%2]").arg(aname).arg(addr), + DeviceMimeConverter::classToMimeType(devClass)); + + return true; +} + +bool TDEioBluetooth::listDevices(TQValueList &list, const KURL &url) +{ + kdDebug() << __func__ << "(" << url << ")" << endl; + + TDEIO::UDSEntry entry; + TDEBluetooth::ObjectManagerImpl::DeviceList dl = manager->getDevices(); + TDEBluetooth::ObjectManagerImpl::DeviceList::Iterator dit = dl.begin(); + for (dit; dit != dl.end(); ++dit) + { + entry.clear(); + listDevice(entry, (*dit), url); + list.append(entry); + } + + return true; +} + +bool TDEioBluetooth::listServices(TQValueList &list, const KURL &url) +{ + kdDebug() << __func__ << "url: " << url << endl; + + TDEIO::UDSEntry entry; + TQString path = url.path(); + + kdDebug() << __func__ << "path: " << path << endl; + + int pos = path.find(TQRegExp("/\\[([0-9A-F]{2}:){5}[0-9A-F]{2}\\]"), 0); + if (pos != -1) + { + + TQString address = path.remove(0, pos + 2).remove(17, path.length()); + kdDebug() << __func__ << "address: " << address << endl; + + TDEBluetooth::ObjectManagerImpl::DeviceList dl = manager->getDevices(); + TDEBluetooth::ObjectManagerImpl::DeviceList::Iterator dit = dl.begin(); + for (dit; dit != dl.end(); ++dit) + { + TDEBluetooth::DeviceImpl *d = new TDEBluetooth::DeviceImpl("org.bluez", (*dit)); + d->setConnection((*(manager->getConnection()))); + + TQT_DBusError dbuserror; + TQString addr = d->getAddress(dbuserror); + if (addr == address) + { + TQStringList uuids = d->getUUIDs(dbuserror); + for (TQStringList::Iterator it = uuids.begin(); + it != uuids.end(); ++it) + { + entry.clear(); + // accepted services OBEX OPP, PCE, FTP + //FIXME temporary disabled +// if ((*it) == "00001105-0000-1000-8000-00805f9b34fb") +// { +// createDirEntry(entry, resolveUUID((*it)), TQString("obexopp:/[%1]/").arg(address), "bluetooth/obex-objectpush-profile"); +// addAtom(entry, TDEIO::UDS_NAME, "obexopp"); +// list.append(entry); +// } +// else if ((*it) == "00001106-0000-1000-8000-00805f9b34fb") +// { +// createDirEntry(entry, resolveUUID((*it)), TQString("obexftp:/[%1]/").arg(address), "bluetooth/obex-ftp-profile"); +// addAtom(entry, TDEIO::UDS_NAME, "obexftp"); +// list.append(entry); +// } + } + break; + } + delete d; + } + } + else + { + ForwardingSlaveBase::error(TDEIO::ERR_MALFORMED_URL, url.url()); + } + + return true; +} + +bool TDEioBluetooth::createDirEntry(TDEIO::UDSEntry &entry, const TQString &name, const TQString &dir, const TQString &mimeType) +{ + kdDebug() << k_funcinfo << endl; + + addAtom(entry, TDEIO::UDS_NAME, name); + if (dir != TQString::null) + { + addAtom(entry, TDEIO::UDS_URL, dir); + } + addAtom(entry, TDEIO::UDS_MIME_TYPE, mimeType); + + if (mimeType == "inode/directory") + addAtom(entry, TDEIO::UDS_FILE_TYPE, S_IFDIR); + else + addAtom(entry, TDEIO::UDS_FILE_TYPE, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + + return true; +} + +void TDEioBluetooth::slotAddDevice(const TQString &path) +{ + kdDebug() << k_funcinfo << endl; + listDir("bluetooth:/"); +} + +void TDEioBluetooth::slotRemoveDevice(const TQString &path) +{ + kdDebug() << __func__ << "(" << path << ")" << endl; + listDir("bluetooth:/"); + +// TQT_DBusError error; +// TDEBluetooth::DeviceImpl *d= new TDEBluetooth::DeviceImpl("org.bluez",path); +// d->setConnection((*(manager->getConnection()))); +// const TQString address = d->getAddress(error); +// delete d; +// +// listDevice(address); +// listEntry(TDEIO::UDSEntry(), true); + +} + +void TDEioBluetooth::slotAddService(const KURL &url, const TQString uuid) +{ + kdDebug() << __func__ << "(URL=" << url << ", UUID=" << uuid << ")" << endl; + +// TQT_DBusError error; +// TDEBluetooth::DeviceImpl *d= new TDEBluetooth::DeviceImpl("org.bluez",path); +// d->setConnection((*(manager->getConnection()))); +// const TQString address = d->getAddress(error); +// TQ_UINT32 devclass = d->getClass(error); +// const TQString devicon = DeviceMimeConverter::classToIconName(devclass); +// delete d; +// +// TQMap::iterator f=qDevicesList.find(address); +// if(f!=qDevicesList.end() && f.data() == devclass) return; + +// listEntry(UDSEntry(), true); + +} + +void TDEioBluetooth::slotAdapterPowerOnChanged(TQString const& path, bool state) +{ + kdDebug() << __func__ << "(" << path << ")" << endl; +// +// TQT_DBusError error; +// TDEBluetooth::DeviceImpl *d= new TDEBluetooth::DeviceImpl("org.bluez",path); +// d->setConnection((*(manager->getConnection()))); +// const TQString address = d->getAddress(error); +// TQ_UINT32 devclass = d->getClass(error); +// const TQString devicon = DeviceMimeConverter::classToIconName(devclass); +// delete d; +// +// TQMap::iterator f=qDevicesList.find(address); +// if(f!=qDevicesList.end() && f.data() == devclass) return; +// qDevicesList.insert(address, devclass); +// +// listDevice(address); +// listEntry(UDSEntry(), true); + +} + +void TDEioBluetooth::addAtom(TDEIO::UDSEntry &entry, TDEIO::UDSAtomTypes type, TQString s) +{ + kdDebug() << k_funcinfo << endl; + TDEIO::UDSAtom atom; + atom.m_uds = type; + atom.m_str = s; + entry.append(atom); +} + +void TDEioBluetooth::addAtom(TDEIO::UDSEntry &entry, TDEIO::UDSAtomTypes type, long l) +{ + kdDebug() << k_funcinfo << endl; + TDEIO::UDSAtom atom; + atom.m_uds = type; + atom.m_long = l; + entry.append(atom); +} + +#include "tdeiobluetooth.moc" diff --git a/src/tdeioslave/bluetooth/tdeiobluetooth.h b/src/tdeioslave/bluetooth/tdeiobluetooth.h new file mode 100644 index 0000000..7b881f2 --- /dev/null +++ b/src/tdeioslave/bluetooth/tdeiobluetooth.h @@ -0,0 +1,56 @@ +//-*-c++-*- +/*************************************************************************** + * Copyright (C) 2003 by Fred Schaettgen * + * kdebluetooth@schaettgen.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#ifndef _TDEIOBT_H_ +#define _TDEIOBT_H_ + +#include +#include + +#include +#include + +class TDEioBluetooth : public TDEIO::ForwardingSlaveBase +{ + Q_OBJECT + +public: + TDEioBluetooth(const TQCString &protocol, const TQCString &pool_socket, const TQCString &app_socket); + virtual ~TDEioBluetooth(); + virtual void closeConnection(); + virtual void stat(const KURL &url); + virtual void listDir(const KURL &url); + +protected: + virtual bool rewriteURL(const KURL &url, KURL &newUrl); + +private: + void createTopLevelEntry(TDEIO::UDSEntry& entry); + bool listDevice(TDEIO::UDSEntry &entry, const TQString &path, const KURL &url); + bool listDevices(TQValueList &list, const KURL &url); + bool listServices(TQValueList &list, const KURL &url); + bool createDirEntry(TDEIO::UDSEntry &entry, const TQString &title, + const TQString &dir = TQString::null, const TQString &mimeType = "inode/directory"); + void addAtom(TDEIO::UDSEntry &entry, TDEIO::UDSAtomTypes type, TQString s); + void addAtom(TDEIO::UDSEntry &entry, TDEIO::UDSAtomTypes type, long l); + + TDEBluetooth::AdapterImpl *adapter; + TDEBluetooth::ObjectManagerImpl *manager; + +private slots: + void slotAddDevice(const TQString &address); + void slotAddService(const KURL &url, const TQString uuid); + void slotRemoveDevice(const TQString &address); + void slotAdapterPowerOnChanged(const TQString & path, bool state) ; + +}; + +#endif //TDEIOBT diff --git a/src/tdeioslave/obex/CMakeLists.txt b/src/tdeioslave/obex/CMakeLists.txt new file mode 100644 index 0000000..d31aa6f --- /dev/null +++ b/src/tdeioslave/obex/CMakeLists.txt @@ -0,0 +1,38 @@ +################################################# +# +# (C) 2018 Emanoil Kotsev +# deloptes (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/src/libtdeobex + ${CMAKE_BINARY_DIR}/src/libtdeobex + ${CMAKE_SOURCE_DIR}/src/tdeioslave/obex + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} + ${DBUS_INCLUDE_DIRS} + ${DBUS_TQT_INCLUDE_DIRS} +) +##### tdeio_bluetooth (static) ############################# +set( target tdeio_obex ) + +tde_add_kpart( ${target} AUTOMOC + SOURCES tdeio_obex.cpp obex.cpp + LINK ${DBUS_TQT_LIBRARIES} tdeio-shared tdeobex-shared obexinterfaces-static + DESTINATION ${PLUGIN_INSTALL_DIR} +) + +install( FILES obexftp.protocol + DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES obexopp.protocol + DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES obex_sidebarentry.desktop + DESTINATION ${DATA_INSTALL_DIR}/konqsidebartng/virtual_folders/services ) \ No newline at end of file diff --git a/src/tdeioslave/obex/README b/src/tdeioslave/obex/README new file mode 100644 index 0000000..a44d55f --- /dev/null +++ b/src/tdeioslave/obex/README @@ -0,0 +1,91 @@ + +OBEX tdeio slave. +--------------- + +At the moment it is mostly working with ober IP, IrDA and the Siemens BFB +transport protocol. The slave ues url's of the form + +obex://hostname:port/path + +for IP connections. It will use the standard OBEX port 650 if the port +argument is omitted. The IP transport also queries for the nss/netdb entry + +obex 650/tcp + +if registered in the services database. (see getent(1) or nsswitch.conf(5)) +The IP transport also tries the port 29650. This one is used in case of +a server running without root privileges. + +The IrDA Transport is accessed via the url format + +obex://irda/path + +In this case device discovery is done and the first device offering +the OBEX hint bit is connected. + +The Bluetooth Transport is accessed via the url format + +obex://bluetooth/path + +In this case device discovery is done and the first device offering +the filesystem browsing profile is connected. + +There exist serial transports for Siemens mobiles. It is currently untested. +In theory it should work. + +A serial transport for the ericcson mobiles is also in the works. It should be +easy to do. It is also difficult to test because I don't know anyone with an +ericsson phone ... + +I have removed the old url format using '!' to separate transport options. +There is now a kcontrol module for configuration. Using this module one can +define 'OBEX hosts' which can be used for hostnames in OBEX url's. Since there +are many configuration options i think this is the best method to access +specific devices. If you know the hardware addresses of your bluetooth or +irda device you can use url's of the form: + +obex://[ef:01:23:45]/ + +if your irda device has the address ef:01:23:45 or + +obex://[ab:cd:ef:01:23:45]/ + +if your bluetooth device has the hardware address ab:cd:ef:01:23:45. +For bluetooth you can also include the rfcomm channel of the folderbrowsing +profile in the usual port field. + +OBEX authentication is currently supported in url's. +More complete authentication configutation will come. + +DEVICE COMPATIBILITY + +The NOKIA 3650 mobile has a firmware bug in some versions. +Mobiles with this bug return invalid XML files for folder listings. This +leads to empty directories. Thie bug is reported to be in at least firmware +version 2.50. The firmware version 3.16 fixed this bug. +Thanks to Fred Schaettgen for testing. + +My Siemens S45i works well with this implementation. + +Connections from and to openobex driven clients or servers will be refused, +since openobex uses a wrong protocol version value in connect +requests/responses. + +DOCUMENTATION + +Hmm, can someone tell me how to write kdehelp documentation? + +DEMO SERVER + +There is an example folderbowsing server in the subfolder +libqobex/qobexfbssrv/. This server and the server api is currently under +heavy development. It listens by default on localhost. You can change the +transport where it listens with -t . It is also password protected +by the password 'fbsserver' For more information think of the wisdom of +the yedi: + +Use the source Luke + +:) + +Have fun! diff --git a/src/tdeioslave/obex/obex.cpp b/src/tdeioslave/obex/obex.cpp new file mode 100644 index 0000000..fb1d81b --- /dev/null +++ b/src/tdeioslave/obex/obex.cpp @@ -0,0 +1,312 @@ +/* + This file is part of tdeio_obex. + + Copyright (c) 2003 Mathias Froehlich + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#include "obex.h" + +#include +#include +#include +#include + +#include + +#include + +#define MINIMUM_UID 500 + +Obex::Obex(TQString &protocol) : + mProtocol(protocol) +{ + kdDebug() << k_funcinfo << endl; + KUser user; + m_effectiveUid = user.uid(); +} + +Obex::~Obex() +{ + kdDebug() << k_funcinfo << endl; +} + +bool Obex::parseURL(const KURL &url, TQString &address, TQString &name, TQString &path) const +{ + kdDebug() << k_funcinfo << endl; + TQString url_path = url.path(+1); + + if (url_path.find(TQRegExp("/\\[([0-9A-F]{2}:){5}[0-9A-F]{2}\\]"), 0) != -1) + { + address = url_path.remove(0, 2); + address = address.remove(17, url_path.length()); + url_path = url_path.remove(0, 18); + kdDebug() << "Obex::parseURL address : " << address << endl; + kdDebug() << "Obex::parseURL url_path : " << url_path << endl; + } + else + return false; + + int i = url_path.find('/', 1); + if (i > 0) + { + name = url_path.mid(1, i - 1); + } + else + { + name = url_path.mid(1); + } + path = url_path; + kdDebug() << "Obex::parseURL path : " << path << endl; + kdDebug() << "Obex::parseURL name : " << name << endl; + + return true; +} + +void Obex::addAtom(UDSEntry &entry, UDSAtomTypes type, const TQString &s) +{ + kdDebug() << k_funcinfo << endl; + UDSAtom atom; + atom.m_uds = type; + atom.m_str = s; + entry.append(atom); +} + +void Obex::addAtom(UDSEntry &entry, UDSAtomTypes type, const long l) +{ + kdDebug() << k_funcinfo << endl; + UDSAtom atom; + atom.m_uds = type; + atom.m_long = l; + entry.append(atom); +} + +void Obex::createTopLevelEntry(UDSEntry &entry) +{ + kdDebug() << k_funcinfo << endl; + + entry.clear(); + addAtom(entry, UDS_NAME, "."); + addAtom(entry, UDS_FILE_TYPE, S_IFDIR); + addAtom(entry, UDS_ACCESS, 0555); + addAtom(entry, UDS_MIME_TYPE, "inode/directory"); + addAtom(entry, UDS_ICON_NAME, "pda_blue"); + addAtom(entry, UDS_USER, "root"); + addAtom(entry, UDS_GROUP, "root"); +} + +void Obex::createDirEntry(UDSEntry &entry, const TQString dir) +{ + kdDebug() << k_funcinfo << endl; + entry.clear(); + addAtom(entry, UDS_NAME, "/" + dir); + addAtom(entry, UDS_FILE_TYPE, S_IFDIR); + addAtom(entry, UDS_ACCESS, 0755); + addAtom(entry, UDS_MIME_TYPE, "inode/directory"); + addAtom(entry, UDS_ICON_NAME, "pda_blue"); + addAtom(entry, UDS_USER, "root"); + addAtom(entry, UDS_GROUP, "root"); +} + +void Obex::slotStatResult(Job *job) +{ + kdDebug() << k_funcinfo << endl; + if (job->error() == 0) + { + StatJob *stat_job = static_cast(job); + m_entryBuffer = stat_job->statResult(); + } + + tqApp->eventLoop()->exitLoop(); +} + +UDSEntry Obex::createUDSEntry(const TQMap &map) +{ + kdDebug() << k_funcinfo << endl; + + UDSEntry entry; + long mode; + uint isFile = 0; + + TQMap::const_iterator mit = map.begin(); + for (mit; mit != map.end(); ++mit) + { + + UDSAtom atom; + if (mit.key() == "Accessed") + { + TQString v = mit.data().toVariant().value.toString(); + atom.m_uds = UDS_ACCESS_TIME; + atom.m_long = stringToTime_t(v); + entry.append(atom); + } + if (mit.key() == "Created") + { + TQString v = mit.data().toVariant().value.toString(); + atom.m_uds = UDS_CREATION_TIME; + atom.m_long = stringToTime_t(v); + entry.append(atom); + } + if (mit.key() == "Group-perm") + { + TQString v = mit.data().toVariant().value.toString(); + if (v.contains('R', FALSE)) + mode |= S_IRGRP; + if (v.contains('W', FALSE)) + mode |= S_IWGRP; + } + if (mit.key() == "Modified") + { + TQString v = mit.data().toVariant().value.toString(); + atom.m_uds = UDS_MODIFICATION_TIME; + atom.m_long = stringToTime_t(v); + entry.append(atom); + } + if (mit.key() == "Size") + { + TQ_UINT64 v = mit.data().toVariant().value.toUInt64(); + atom.m_uds = UDS_SIZE; + atom.m_long = v; + entry.append(atom); + } + if (mit.key() == "Name") + { + TQString v = mit.data().toVariant().value.toString(); + atom.m_uds = UDS_NAME; + atom.m_str = v; + entry.append(atom); + } + if (mit.key() == "Other-perm") + { + TQString v = mit.data().toVariant().value.toString(); + if (v.contains('R', FALSE)) + mode |= S_IROTH; + if (v.contains('W', FALSE)) + mode |= S_IWOTH; + } + if (mit.key() == "Type") + { + TQString v = mit.data().toVariant().value.toString(); + if (v == "folder") + { + isFile = 1; + } + if (v == "file") + { + TQString v = mit.data().toVariant().value.toString(); + isFile = 2; + } + } + if (mit.key() == "User-perm") + { + TQString v = mit.data().toVariant().value.toString(); + if (v.contains('R', FALSE)) + mode |= S_IRUSR; + if (v.contains('W', FALSE)) + mode |= S_IWUSR; + } + } + + if (isFile == 1) + { + if (mode & S_IRUSR) + mode |= S_IXUSR; + if (mode & S_IRGRP) + mode |= S_IXGRP; + if (mode & S_IROTH) + mode |= S_IXOTH; + + UDSAtom atom; + atom.m_uds = UDS_ACCESS; + atom.m_long = mode; + entry.append(atom); + + // set the file type + atom.m_uds = UDS_FILE_TYPE; + atom.m_long = S_IFDIR; + entry.append(atom); + } + else if (isFile == 2) + { + UDSAtom atom; + atom.m_uds = UDS_ACCESS; + atom.m_long = mode; + entry.append(atom); + + // set the file type + atom.m_uds = UDS_FILE_TYPE; + atom.m_long = S_IFREG; + entry.append(atom); + } + else + { + //FIXME fall back + } + return entry; +} + +time_t Obex::stringToTime_t(TQString str) +{ + kdDebug() << k_funcinfo << endl; + + str.insert(13, ':'); + str.insert(11, ':'); + str.insert(6, '-'); + str.insert(4, '-'); + TQDateTime time = TQDateTime::fromString(str, TQt::ISODate); + + return time.toTime_t(); +} + +UDSEntry Obex::extractUrlInfos(const KURL &url) +{ + kdDebug() << k_funcinfo << endl; + + m_entryBuffer.clear(); + + StatJob *job = stat(url, false); + connect(job, TQT_SIGNAL(result(Job *)), this, TQT_SLOT(slotStatResult(Job *))); + tqApp->eventLoop()->enterLoop(); + + UDSEntry::iterator it = m_entryBuffer.begin(); + UDSEntry::iterator end = m_entryBuffer.end(); + + UDSEntry infos; + + for (; it != end; ++it) + { + switch ((*it).m_uds) + { + case UDS_ACCESS: + case UDS_USER: + case UDS_GROUP: + case UDS_CREATION_TIME: + case UDS_MODIFICATION_TIME: + case UDS_ACCESS_TIME: + infos.append(*it); + break; + default: + break; + } + } + + addAtom(infos, UDS_LOCAL_PATH, url.path()); + + return infos; +} + +#include "obex.moc" diff --git a/src/tdeioslave/obex/obex.h b/src/tdeioslave/obex/obex.h new file mode 100644 index 0000000..bc6d3cc --- /dev/null +++ b/src/tdeioslave/obex/obex.h @@ -0,0 +1,66 @@ +/* + This file is part of tdeio_obex. + + Copyright (c) 2003 Mathias Froehlich + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef OBEX_H +#define OBEX_H + + +#include +#include +#include +#include + +#include + +#include + +using namespace TDEIO; + +class Obex : public TQObject +{ + Q_OBJECT + +public: + Obex(TQString & mProtocol); + virtual ~Obex(); + bool parseURL(const KURL &url, TQString &address, TQString &name, TQString &path) const; + void createTopLevelEntry(UDSEntry &entry); + void createDirEntry(UDSEntry &entry, const TQString dir); + UDSEntry createUDSEntry( const TQMap< TQString, TQT_DBusData > &map ); + bool changeWorkingDirectory( const TQString& to ); + +private slots: + void slotStatResult(Job *job); + +private: + + UDSEntry extractUrlInfos(const KURL &url); + UDSEntry m_entryBuffer; + void addAtom(UDSEntry &entry, UDSAtomTypes type, const TQString &s); + void addAtom(UDSEntry &entry, UDSAtomTypes type, const long l); + + time_t stringToTime_t( TQString ); + + long m_effectiveUid; + TQString mProtocol; +}; + +#endif diff --git a/src/tdeioslave/obex/obex_sidebarentry.desktop b/src/tdeioslave/obex/obex_sidebarentry.desktop new file mode 100644 index 0000000..9bc0178 --- /dev/null +++ b/src/tdeioslave/obex/obex_sidebarentry.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Link +URL=obexftp:/ +Icon=tdebluez +Name=Bluetooth Browser +Name[et]=Bluetoothi brauser +Open=false +X-TDE-TreeModule=Directory +X-TDE-KonqSidebarModule=konqsidebar_tree diff --git a/src/tdeioslave/obex/obexftp.protocol b/src/tdeioslave/obex/obexftp.protocol new file mode 100644 index 0000000..3262d4b --- /dev/null +++ b/src/tdeioslave/obex/obexftp.protocol @@ -0,0 +1,52 @@ +[Protocol] +# The executable +exec=tdeio_obex +# This protocol name +protocol=obexftp +Parent=system:/ + +# input/output can be one of: filesystem, stream, none +input=none +output=filesystem +maxInstances=1 + +# capabilities for this protocol +reading=true +writing=true +makedir=true +deleting=true +moving=false +linking=false + +# What to list ... FIXME work out how ... +listing=Name,Type,Size,Date,AccessDate,Access,Owner,Group + +# Can be source protocol +source=false + +Description=A tdeioslave for OBEX connections +Description[bg]=tdeioslave за OBEX връзки +Description[br]=Ur c'hioslave evit ar c'hevreadennoù OBEX +Description[ca]=Un tdeioslave per a les connexions OBEX +Description[da]=En tdeioslave til OBEX-forbindelser +Description[de]=Ein tdeioslave für OBEX-Verbindungen +Description[el]=Ένας υπηρέτης tdeio για συνδέσεις OBEX +Description[es]=Un 'tdeioslave' para conexiones OBEX +Description[et]=OBEX-ühenduste I/O-moodul +Description[fr]=Un tdeioslave pour les connexions OBEX +Description[it]=Un tdeioslave per connessioni OBEX +Description[ja]=OBEX 接続のための tdeioslave +Description[ka]=tdeioslave OBEX კავშირებისთვის +Description[nl]=Een tdeioslave for OBEX-verbindingen +Description[pl]=Wtyczka protokołu dla połączeń OBEX +Description[pt]=Um 'tdeioslave' para ligações OBEX +Description[pt_BR]=Um 'tdeioslave' para ligações OBEX +Description[sr]=tdeioslave за OBEX везе +Description[sr@Latn]=tdeioslave za OBEX veze +Description[sv]=En I/O-slav för OBEX-anslutningar +Description[ta]=ஒரு OBEX இணைப்பிற்க்காண tdeioslave. +Description[tr]=OBEX bağlantıları için bir tdeioslave +Description[xx]=xxA tdeioslave for OBEX connectionsxx +Icon=remote + +DocPath=tdebluez/components.tdeio_obex.html diff --git a/src/tdeioslave/obex/obexopp.protocol b/src/tdeioslave/obex/obexopp.protocol new file mode 100644 index 0000000..964bab9 --- /dev/null +++ b/src/tdeioslave/obex/obexopp.protocol @@ -0,0 +1,52 @@ +[Protocol] +# The executable +exec=tdeio_obex +# This protocol name +protocol=obexopp +Parent=system:/ + +# input/output can be one of: filesystem, stream, none +input=none +output=none +maxInstances=1 + +# capabilities for this protocol +reading=false +writing=true +makedir=false +deleting=false +moving=false +linking=false + +# What to list ... FIXME work out how ... +listing=Name,Type,Size,Date,AccessDate,Access,Owner,Group + +# Can be source protocol +source=false + +Description=A tdeioslave for OBEX connections +Description[bg]=tdeioslave за OBEX връзки +Description[br]=Ur c'hioslave evit ar c'hevreadennoù OBEX +Description[ca]=Un tdeioslave per a les connexions OBEX +Description[da]=En tdeioslave til OBEX-forbindelser +Description[de]=Ein tdeioslave für OBEX-Verbindungen +Description[el]=Ένας υπηρέτης tdeio για συνδέσεις OBEX +Description[es]=Un 'tdeioslave' para conexiones OBEX +Description[et]=OBEX-ühenduste I/O-moodul +Description[fr]=Un tdeioslave pour les connexions OBEX +Description[it]=Un tdeioslave per connessioni OBEX +Description[ja]=OBEX 接続のための tdeioslave +Description[ka]=tdeioslave OBEX კავშირებისთვის +Description[nl]=Een tdeioslave for OBEX-verbindingen +Description[pl]=Wtyczka protokołu dla połączeń OBEX +Description[pt]=Um 'tdeioslave' para ligações OBEX +Description[pt_BR]=Um 'tdeioslave' para ligações OBEX +Description[sr]=tdeioslave за OBEX везе +Description[sr@Latn]=tdeioslave za OBEX veze +Description[sv]=En I/O-slav för OBEX-anslutningar +Description[ta]=ஒரு OBEX இணைப்பிற்க்காண tdeioslave. +Description[tr]=OBEX bağlantıları için bir tdeioslave +Description[xx]=xxA tdeioslave for OBEX connectionsxx +Icon=remote + +DocPath=tdebluez/components.tdeio_obex.html diff --git a/src/tdeioslave/obex/tdeio_obex.cpp b/src/tdeioslave/obex/tdeio_obex.cpp new file mode 100644 index 0000000..fbd2fb5 --- /dev/null +++ b/src/tdeioslave/obex/tdeio_obex.cpp @@ -0,0 +1,548 @@ +/* + This file is part of tdeio_obex. + + Copyright (c) 2003 Mathias Froehlich + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#include +#include +//#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include "tdeio_obex.h" + +static const TDECmdLineOptions options[] = { { "+protocol", I18N_NOOP( + "Protocol name"), 0 }, { "+pool", I18N_NOOP("Socket name"), 0 }, { + "+app", I18N_NOOP("Socket name"), 0 }, TDECmdLineLastOption }; + +extern "C" { + int KDE_EXPORT kdemain( int argc, char **argv ) + { + // TDEApplication is necessary to use other ioslaves + putenv(strdup("SESSION_MANAGER=")); + TDECmdLineArgs::init(argc, argv, "tdeio_obex", 0, 0, 0, 0); + TDECmdLineArgs::addCmdLineOptions( options ); + TDEApplication app( false, false, false ); + // We want to be anonymous even if we use DCOP + app.dcopClient()->attach(); + + TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs(); + ObexProtocol slave( args->arg(0), args->arg(1), args->arg(2) ); + slave.dispatchLoop(); + return 0; + } +} + +ObexProtocol::ObexProtocol(const TQCString &protocol, const TQCString &pool_socket, const TQCString &app_socket) : + SlaveBase(protocol, pool_socket, app_socket) +{ + kdDebug() << k_funcinfo << endl; + mChannel = 0; + mAddress = TQString::null; + mSessionPath = TQString(); + mFileTransfer = 0; + mSessionProperties = 0; + mSession = 0; + mClient = 0; + mProtocol = protocol; + mHost = TQString::null; + mConnected = false; + + mManager = new TDEObex::ObexObjectManagerImpl("org.bluez.obex", "/"); + if (!mManager->isConnectedToDBUS()) + { + TQString err = "ObexObjectManager is not connected to DBus"; + tqDebug(err); + // infoMessage(i18n("Error")); + TDEIO::SlaveBase::error(TDEIO::ERR_COULD_NOT_CONNECT, err); + exit(); + } + + kdDebug() << "ObexProtocol::ObexProtocol DBus connection: " << (*(mManager->getConnection())).uniqueName() << endl; + + if (mProtocol == "obexftp" || mProtocol == "obexopp") + { + obex = new Obex(mProtocol); + // mConnected = connectObex(); + } + else + exit(); + + if (!mClient) + mClient = mManager->getClient(); + if (!mClient) + { + TDEIO::SlaveBase::error(TDEIO::ERR_COULD_NOT_CONNECT, "ObexClient was not created"); + exit(); + } + +} + +ObexProtocol::~ObexProtocol() +{ + kdDebug() << k_funcinfo << endl; + if (mConnected) + closeObex(); + if (obex) + delete obex; + if (mManager) + delete mManager; +} + +void ObexProtocol::closeConnection() +{ + kdDebug() << k_funcinfo << endl; + closeObex(); +} + +void ObexProtocol::closeObex() +{ + kdDebug() << k_funcinfo << endl; + + TQT_DBusError dbuserror; + if (mConnected && !mSessionPath.isEmpty()) + { + // infoMessage(i18n("Disconnecting")); + if (!mClient->RemoveSession(mSessionPath, dbuserror)) + { + if (dbuserror.isValid()) + TDEIO::SlaveBase::error(TDEIO::ERR_COULD_NOT_CONNECT, dbuserror.message()); + } + // infoMessage(i18n("Disconnected")); + } + + if (mFileTransfer) + delete mFileTransfer; + if (mSessionProperties) + delete mSessionProperties; + if (mSession) + delete mSession; + if (mClient) + delete mClient; + mConnected = false; + + exit(); +} + +//void ObexProtocol::openConnection() +//{ +// kdDebug() << k_funcinfo << endl; +// +//} + +bool ObexProtocol::connectObex() +{ + kdDebug() << k_funcinfo << endl; + + TQT_DBusError dbuserror; + + TQT_DBusVariant obexprot; + if (mProtocol == "obexftp") + obexprot.value = TQT_DBusData::fromString("00001106-0000-1000-8000-00805f9b34fb"); + else if (mProtocol == "obexopp") + obexprot.value = TQT_DBusData::fromString("00001105-0000-1000-8000-00805f9b34fb"); + else if (mProtocol == "obexmap") + obexprot.value = TQT_DBusData::fromString("00001134-0000-1000-8000-00805f9b34fb"); + else if (mProtocol == "obexpbap") + obexprot.value = TQT_DBusData::fromString("00001130-0000-1000-8000-00805f9b34fb"); + else if (mProtocol == "obexsync") + obexprot.value = TQT_DBusData::fromString("00001104-0000-1000-8000-00805f9b34fb"); + obexprot.signature = obexprot.value.buildDBusSignature(); + TQMap args; + args.insert(TQString("Target"), obexprot); + + if (mSessionPath.isEmpty()) + { + kdDebug() << "ObexProtocol::connectObex : trying to create session" << endl; + if (!mClient->CreateSession(mAddress, args, mSessionPath, dbuserror)) + { + TDEIO::SlaveBase::error(TDEIO::ERR_COULD_NOT_CONNECT, i18n("Could not create session for %1.").arg(mAddress)); + return false; + } + } + + kdDebug() << "ObexProtocol::connectObex mSessionPath: " << mSessionPath << endl; + if (!mSession) + { + mSession = new org::bluez::obex::Session1Proxy("org.bluez.obex", mSessionPath); + mSession->setConnection((*(mManager->getConnection()))); + + mSessionProperties = new org::freedesktop::DBus::PropertiesProxy("org.bluez", mSessionPath); + mSessionProperties->setConnection((*(mManager->getConnection()))); + + connect(mSessionProperties, SIGNAL(PropertiesChanged ( const TQString&, const TQMap< TQString, TQT_DBusVariant >&, const TQStringList& )), + this, SLOT(slotPropertiesChanged ( const TQString& , const TQMap< TQString, TQT_DBusVariant >&, const TQStringList& ))); + + mFileTransfer = new org::bluez::obex::FileTransfer1Proxy("org.bluez.obex", mSessionPath); + mFileTransfer->setConnection((*(mManager->getConnection()))); + + } + if (mClient != 0 && mSession != 0 && mFileTransfer != 0) + mConnected = true; + return mConnected; +} + +void ObexProtocol::listDir(const KURL &url) +{ + kdDebug() << k_funcinfo << endl; + kdDebug() << "utl: " << url.url() << endl; + kdDebug() << "path: " << url.path() << endl; + + if (url.path().length() <= 1) + { + TDEIO::SlaveBase::error(TDEIO::ERR_MALFORMED_URL, url.prettyURL()); + finished(); + return; + } + + TQString address, name, path; + bool ok = obex->parseURL(url, address, name, path); + + if (!ok || address.isEmpty()) + { + TDEIO::SlaveBase::error(TDEIO::ERR_MALFORMED_URL, url.prettyURL()); + finished(); + return; + } + mAddress = address; + + kdDebug() << k_funcinfo << " address " << mAddress << endl; + kdDebug() << k_funcinfo << " name " << name << endl; + kdDebug() << k_funcinfo << " path " << path << endl; + kdDebug() << k_funcinfo << " at line " << __LINE__ << endl; + + if (!mConnected) + { + if (!connectObex()) + { + finished(); + return; + } + } + + if (!path.isEmpty()) + { + if (!changeWorkingDirectory(path)) + { + TDEIO::SlaveBase::error(TDEIO::ERR_CANNOT_OPEN_FOR_READING, path); + + finished(); + closeObex(); + return; + } + } + + kdDebug() << k_funcinfo << " at line " << __LINE__ << endl; + TQT_DBusDataList folderinfo; + TQT_DBusError dbuserror; + if (!mFileTransfer->ListFolder(folderinfo, dbuserror)) + { + if (dbuserror.isValid()) + { + TDEIO::SlaveBase::error(TDEIO::ERR_CANNOT_OPEN_FOR_READING, i18n("%1.\n%2").arg(url.prettyURL()).arg(dbuserror.message())); + } + else + { + TDEIO::SlaveBase::error(TDEIO::ERR_CANNOT_OPEN_FOR_READING, url.prettyURL()); + } + + finished(); + closeObex(); + return; + } + + TDEIO::UDSEntryList entries; + entries.clear(); + + TQValueList vl = folderinfo.toTQValueList(); + TQValueList::Iterator dit = vl.begin(); + for (dit; dit != vl.end(); ++dit) + { + bool ok = false; + TQMap map = (*dit).toStringKeyMap(&ok).toTQMap(); + if (!ok) + { + kdDebug() << k_funcinfo << " failed " << endl; + continue; + } + TDEIO::UDSEntry entry = obex->createUDSEntry(map); + entries.append(entry); + kdDebug() << k_funcinfo << " at line " << __LINE__ << endl; + } + + listEntries(entries); + listEntry(UDSEntry(), true); // ready + + finished(); + closeObex(); +} + +void ObexProtocol::stat(const KURL &url) +{ + kdDebug() << k_funcinfo << " url: " << url << endl; + + TQString address, name, path; + bool ok = obex->parseURL(url, address, name, path); + kdDebug() << k_funcinfo << " addr: " << address << endl; + kdDebug() << k_funcinfo << " name: " << name << endl; + kdDebug() << k_funcinfo << " path: " << path << endl; + + if (!ok || address.isEmpty()) + { + error(TDEIO::ERR_MALFORMED_URL, url.prettyURL()); + return; + } + + TDEIO::UDSEntry entry; + if (path.isEmpty() || path == "/") + { + // The root is "virtual" - it's not a single physical directory + obex->createTopLevelEntry(entry); + } + else + { + obex->createDirEntry(entry, url.url()); + } + statEntry(entry); + finished(); +} + +void ObexProtocol::get(const KURL& url) +{ + kdDebug() << k_funcinfo << endl; + + if (!mFileTransfer) + { + TDEIO::SlaveBase::error(TDEIO::ERR_INTERNAL, i18n("Could not get file: %1. No file transport").arg(url.prettyURL())); + return; + } + if (!mConnected) + { + if (!connectObex()) + { + finished(); + return; + } + } +// TQT_DBusError dbuserror; +// if (!mFileTransfer->GetFile(url.url(), dbuserror)) { +// TDEIO::SlaveBase::error(TDEIO::ERR_INTERNAL, i18n("Could not get file: %1.").arg(dbuserror.message())); +// } +} + +void ObexProtocol::copy(const KURL &src, const KURL &dest, int permissions, bool overwrite) +{ + kdDebug() << k_funcinfo << endl; + // obex->copy(src, dest, permissions, overwrite); + if (!mConnected) + { + if (!connectObex()) + { + finished(); + return; + } + } +} + +void ObexProtocol::put(const KURL& url, int permissions, bool overwrite, bool resume) +{ + kdDebug() << k_funcinfo << endl; + if (!mConnected) + { + if (!connectObex()) + { + finished(); + return; + } + } + if (!mFileTransfer) + { + TDEIO::SlaveBase::error(TDEIO::ERR_INTERNAL, i18n("Could not put file: %1. No file transport").arg(url.prettyURL())); + return; + } + +// +// TQT_DBusError dbuserror; +// if (!mFileTransfer->PutFile(url.url(), dbuserror)) { +// TDEIO::SlaveBase::error(TDEIO::ERR_INTERNAL, i18n("Could not put file: %1.").arg(dbuserror.message())); +// } + +} + +void ObexProtocol::del(const KURL &url, bool isfile) +{ + kdDebug() << k_funcinfo << endl; + + if (!isfile) + { + TDEIO::SlaveBase::error(TDEIO::ERR_INTERNAL, i18n("Only files can be deleted. Request to delete: %1").arg(url.prettyURL())); + return; + } + if (!mConnected) + { + if (!connectObex()) + { + finished(); + return; + } + } + if (!mFileTransfer) + { + TDEIO::SlaveBase::error(TDEIO::ERR_INTERNAL, i18n("Could not delete file: %1. No file transport").arg(url.prettyURL())); + return; + } + + TQT_DBusError dbuserror; + if (!mFileTransfer->Delete(url.url(), dbuserror)) + { + TDEIO::SlaveBase::error(TDEIO::ERR_INTERNAL, i18n("Could not delete file: %1.").arg(dbuserror.message())); + } + +} + +//void ObexProtocol::rename(const KURL& src, const KURL& dest, bool overwrite) +//{ +// kdDebug() << k_funcinfo << endl; +// // obex->rename(src, dest, overwrite); +// +//} + +void ObexProtocol::mkdir(const KURL&url, int permissions) +{ + kdDebug() << k_funcinfo << endl; + if (!mConnected) + { + if (!connectObex()) + { + finished(); + return; + } + } + if (!mFileTransfer) + { + TDEIO::SlaveBase::error(TDEIO::ERR_INTERNAL, i18n("Could not create directory: %1. No file transport").arg(url.prettyURL())); + return; + } + + TQT_DBusError dbuserror; + if (!mFileTransfer->CreateFolder(url.url(), dbuserror)) + { + TDEIO::SlaveBase::error(TDEIO::ERR_INTERNAL, i18n("Could not create directory: %1.").arg(dbuserror.message())); + } + +} + +bool ObexProtocol::changeWorkingDirectory(const TQString& dir) +{ + kdDebug() << "ObexProtocol::changeWorkingDirectory( " << dir << " )" << endl; + if (!dir.startsWith("/")) + { + TDEIO::SlaveBase::error(TDEIO::ERR_MALFORMED_URL, i18n("Could not change directory: %1. Directory should start with \"/\"").arg(dir)); + return false; + } + if (!mConnected) + { + if (!connectObex()) + { + finished(); + return false; + } + } + if (!mFileTransfer) + { + TDEIO::SlaveBase::error(TDEIO::ERR_INTERNAL, i18n("Could not change directory: %1. No file transport").arg(dir)); + return false; + } + + TQT_DBusError dbuserror; + if (!mFileTransfer->ChangeFolder(dir, dbuserror)) + { + TDEIO::SlaveBase::error(TDEIO::ERR_INTERNAL, i18n("Could not change directory: %1.").arg(dbuserror.message())); + return false; + } + + return true; +} + +void ObexProtocol::slotPropertiesChanged(const TQString& interface, const TQMap< + TQString, TQT_DBusVariant>& changed_properties, const TQStringList& invalidated_properties) +{ + kdDebug() << k_funcinfo << endl; + kdDebug() << interface << endl; + + if (interface == "org.bluez.obex.Session1") + { + TQMap::const_iterator it; + for (it = changed_properties.begin(); it != changed_properties.end(); + ++it) + { + bool ok = false; + if (it.key() == "Source") + emit sessionSourceChanged(mSessionPath, it.data().value.toBool(&ok)); + else if (it.key() == "Destination") + emit sessionDestinationChanged(mSessionPath, it.data().value.toString(&ok)); + else if (it.key() == "Channel") + emit sessionChannelChanged(mSessionPath, it.data().value.toByte(&ok)); + else if (it.key() == "Target") + emit sessionTargetChanged(mSessionPath, it.data().value.toString(&ok)); + else if (it.key() == "Root") + emit sessionRootChanged(mSessionPath, it.data().value.toString(&ok)); + else + continue; + if (!ok) + tqDebug("ObjectManagerImpl::slotPropertiesChanged conversion failed"); + } + } + + if (interface == "org.bluez.obex.FileTransfer1" || interface == "org.bluez.obex.Transfer1") + { + TQMap::const_iterator it; + for (it = changed_properties.begin(); it != changed_properties.end(); + ++it) + { + bool ok = false; + if (it.key() == "Size") + emit transferSizeChanged(mSessionPath, it.data().value.toUInt64(&ok)); + else if (it.key() == "Status") + emit transferStatusChanged(mSessionPath, it.data().value.toString(&ok)); + else if (it.key() == "Transferred") + emit transferTransferredChanged(mSessionPath, it.data().value.toUInt64(&ok)); + else if (it.key() == "Time") + emit transferTimeChanged(mSessionPath, it.data().value.toUInt64(&ok)); + else if (it.key() == "Filename") + emit transferFilenameChanged(mSessionPath, it.data().value.toString(&ok)); + else + continue; + if (!ok) + tqDebug("ObjectManagerImpl::slotPropertiesChanged conversion failed"); + } + } +} + +#include "tdeio_obex.moc" diff --git a/src/tdeioslave/obex/tdeio_obex.h b/src/tdeioslave/obex/tdeio_obex.h new file mode 100644 index 0000000..afa4b2e --- /dev/null +++ b/src/tdeioslave/obex/tdeio_obex.h @@ -0,0 +1,135 @@ +/* + This file is part of tdeio_obex. + + Copyright (c) 2003 Mathias Froehlich + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#ifndef TDEIO_OBEX_H +#define TDEIO_OBEX_H + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "obex.h" + +class ObexProtocol: public TQObject, public TDEIO::SlaveBase { + + Q_OBJECT + +public: + ObexProtocol(const TQCString &protcol, const TQCString &pool_socket, + const TQCString &app_socket); + virtual ~ObexProtocol(); +// virtual void openConnection(); + virtual void closeConnection(); + virtual void stat(const KURL& url); + virtual void listDir(const KURL& url); + virtual void get(const KURL& url); + virtual void copy(const KURL &src, const KURL &dest, int permissions, + bool overwrite); + virtual void put(const KURL& url, int permissions, bool overwrite, + bool resume); + virtual void del(const KURL &url, bool isfile); + // virtual void chmod(const KURL& url, int permissions); +// virtual void rename(const KURL& src, const KURL& dest, bool overwrite); + virtual void mkdir(const KURL&url, int permissions); + +private: + // Private variables + /** True if ioslave is connected to server. */ + bool mConnected; + + /** Host we are connected to. */ + TQString mHost; + + /** + The protocol to be used. + */ + TQString mProtocol; + + /** + Pointer to the obex obejct. + */ + Obex *obex; + + /** + Pointer to the obex client class. + */ + org::bluez::obex::Client1Proxy* mClient; + + /** + Pointer to the obex session class. + */ + org::bluez::obex::Session1Proxy* mSession; + org::freedesktop::DBus::PropertiesProxy* mSessionProperties; + org::bluez::obex::FileTransfer1Proxy* mFileTransfer; + TQT_DBusObjectPath mSessionPath; + TQString mAddress; + + /** + Pointer to the obex agent manager class. + */ + TDEObex::ObexObjectManagerImpl* mManager; + + /** Channel we are connected to. */ + int mChannel; + + struct Status { + int code; + TDEIO::filesize_t size; + TQString text; + }; + +private: + // private methods + + /** + Helper functions. + */ + bool connectObex(); + void closeObex(); + bool changeWorkingDirectory(const TQString& to); + +private slots: + void slotPropertiesChanged(const TQString& interface, + const TQMap& changed_properties, + const TQStringList& invalidated_properties); + +signals: + void sessionSourceChanged(TQT_DBusObjectPath &path,bool ok); + void sessionDestinationChanged(TQT_DBusObjectPath &path, TQString value); + void sessionChannelChanged(TQT_DBusObjectPath &path, TQ_UINT8 value); + void sessionTargetChanged(TQT_DBusObjectPath &path, TQString value); + void sessionRootChanged(TQT_DBusObjectPath &path, TQString value); + + void transferSizeChanged(TQT_DBusObjectPath &path, TQ_UINT64 value); + void transferStatusChanged(TQT_DBusObjectPath &path, TQString value); + void transferTransferredChanged(TQT_DBusObjectPath &path, TQ_UINT64 value); + void transferTimeChanged(TQT_DBusObjectPath &path, TQ_UINT64 value); + void transferFilenameChanged(TQT_DBusObjectPath &path, TQString value); +}; + +#endif