April 15, 2007

Starting with Solid Bluetooth

Today I've been starting to try using the Solid library, and concretly the Bluetooth support on it and the API it exposes. With this purpose I've set up a little CMake project that justs gets a reference to the Bluetooth Manager and list remote devices it finds in a single device scan. The output of this program gives the same information (at least the address) you would retrieve using the command line:

$ hcitool scan

You need to follow some steps before trying this code:

  1. You must have the latest CVS version of bluez installed and running on your system. You need the libs and utils modules; the following commands would get you those modules in the current directory:

    • $ cvs -z3 -d:pserver:anonymous@bluez.cvs.sourceforge.net:/cvsroot/bluez co -P libs
    • $ cvs -z3 -d:pserver:anonymous@bluez.cvs.sourceforge.net:/cvsroot/bluez co -P utils

    You should compile the libs first and then the utils. To build them execute the following commands on each folder:
    $ ./bootStrap
    $ ./configure
    $ make
    # make install
  2. Of course it's mandatory to have a working kde 4 development environment (be sure to check the SVN dashboard to know which revision builds), and maybe set kdevelop 3 also. If you've already setup a kde4 environment you still have to recompile kdebase to enable BlueZ support.

  3. Finally, to ensure that you've your Solid Bluetooth support setup correctly execute the following command (be sure that the command is executed using the kde 4 libraries, if you followed the tutorial it's a matter of running it as the kde-devel user):
    $ kcmshell kcm_solid
    And there should pop a window where you must find something like the following:

Once you've set that up, and only if you saw the BlueZ backend on the last image, create a new folder to contain your project, let's call it SolidBZ/. In that folder we are putting three files, namely:

  • CMakeLists.txt : This is the definition file for CMake, and it defines the library requirements and source files of our project.
  • main.h : In this file we'll define a single class that will provide slots to handle the signals emited by the Solid::BluetoothManager.
  • main.cpp :The implementation of the previous class and the main function.
Let's see each of those files and comment them a bit:

CMakeLists.txt

PROJECT(SolidBZ)
#Finds KDE4 libraries, include them and set includes
FIND_PACKAGE(KDE4 REQUIRED)
INCLUDE(KDE4Defaults)
INCLUDE_DIRECTORIES(${KDE4_INCLUDES})

#List of source files
SET(SolidBZ_sources main.cpp)

#Run the Meta Object Compiler
KDE4_AUTOMOC( ${SolidBZ_sources} )

#Create the executable
KDE4_ADD_EXECUTABLE(SolidBZ ${SolidBZ_sources})
TARGET_LINK_LIBRARIES(SolidBZ ${KDE4_SOLID_LIBS} )

In the first line we set a name for our project, then instruct CMake to find the KDE 4 package. On line 3 we include all defaults for KDE4 and set includes for the compiler on line 4. The SET command creates a variable holding the name of our single source file. The KDE4_AUTOMOC directive is of special importance because we will be creating a kind of QObject and need the Meta Object Compiler to create signals implementations and some other stuff. Last two lines set the executable to create with the source from where it should be taken, and finally links it with the Solid library.

Now let's see our header:

main.h


#ifndef __MAIN
#define __MAIN

#include <qcoreapplication>

class Handler:public QCoreApplication
{
Q_OBJECT
public:
QEventLoop m_loop;
Handler(
int argc, char **argv):QCoreApplication(argc,argv){};
public Q_SLOTS:
void slotBluetoothDeviceFound( const QString &nombre, int deviceClass, int rssi );
void slotBluetoothDiscoveryCompleted();
void slotBluetoothDiscoveryStarted();
};
#endif

This class is pretty straightforward, but maybe you're puzzled by the blue words... read this to understand why they are there.

main.cpp


#include "main.h"

#include <qstring>

#include <kdebug.h>
#include <kcomponentdata.h>

#include <solid/control/bluetoothmanager.h>
#include <solid/control/bluetoothinterface.h>

using namespace std;

int main(int argc, char **argv)
{
Handler handler(argc,argv);
KComponentData componentData( "SolidBZ" );

Solid::BluetoothManager &manager = Solid::BluetoothManager::self();
Solid::BluetoothInterface adapter = manager.findBluetoothInterface( manager.defaultInterface());

QObject::connect( &adapter, SIGNAL( discoveryStarted() ),
&handler, SLOT( slotBluetoothDiscoveryStarted() ) );
QObject::connect( &adapter, SIGNAL( remoteDeviceFound( const QString &, int, int ) ),
&handler, SLOT( slotBluetoothDeviceFound( const QString &, int, int ) ) );
QObject::connect( &adapter, SIGNAL( discoveryCompleted() ),
&handler, SLOT( slotBluetoothDiscoveryCompleted() ) );

adapter.discoverDevices();
kDebug() << "Searching ..." << endl;
handler.m_loop.exec();
}

void Handler::slotBluetoothDeviceFound(const QString & nombre, int , int )
{
kDebug() << "Remote device found : " << nombre << endl;
}

void Handler::slotBluetoothDiscoveryCompleted()
{
kDebug() << "Discovery completed" << endl;
m_loop.exit();
}

void Handler::slotBluetoothDiscoveryStarted()
{
kDebug() << "Discovery started" << endl;
}

#include "main.moc"


I'll avoid commenting the slots code and focus on the main function. First we get an instance of Handler (that will also set some things up) and one KComponentData that does not so many things here but is needed for kDebug to work correctly. We also create a QEventLoop instance so our program continues running and is able to receive the signals we are looking for.

Now, we use the static self() method from the Solid::BluetoothManager class to get the registered manager (from the BlueZ backend), and using it obtain the default Solid::BluetoothInterface which is the first local device detected by BlueZ.

Once we have a reference to our default device we connect the declared slots on the Handler class to the signals generated by the BluetoothManager using the QObject::connect static method.

As a last step, we call the discoverDevices method on our BluetoothManager, which is asynchronous and will use the signals we've just registered with our slots to notify the results.

In addition, we call exec() on the QEventLoop so our program can survive to get the responses, and also exit() on that same class to gracefully stop the program once we receive the discoveryCompleted signal.

To compile all this you only have to cd into the SolidBZ/ folder (in the kde4 environment) and execute:
$ cmake -f CMakeLists.txt
$ make
And that's it, now just run the resulting SolidBZ executable and watch your remote devices on ASCII ;)

1 comment:

willi said...

where can i find the api regarding solid::bluetooth?

ive been looking to interact with some of my bt gadgets, but im kinda at a loss when it comes to navigating kde-devel things...

Creative Commons License All this site has a Creative Commons License.