Logo Search packages:      
Sourcecode: kcm-tablet version File versions  Download package

devicehandler.cpp

/*
 * Copyright 2009, 2010 Jörg Ehrichs <joerg.ehichs@gmx.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.
 *
 * 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, see <http://www.gnu.org/licenses/>.
 */

#include "devicehandler.h"
#include "deviceadaptor.h"
#include "deviceinterface.h"
#include "wacominterface.h"

//KDE includes
#include <KDE/KConfigGroup>
#include <KDE/KSharedConfig>
#include <KDE/KStandardDirs>
#include <KDE/KDebug>

//Qt includes
#include <QtCore/QProcess>
#include <QtCore/QRegExp>

namespace Wacom
{
/**
 * Private class for the DeviceHandler d-pointer.
 */
00038 class DeviceHandlerPrivate
{
public:
00041     KSharedConfig::Ptr companyConfig;        /**< Ref Pointer for the data device list with all known tablet company information */
00042     DeviceInterface   *curDevice;            /**< Handler for the current device to get/set its configuration */
00043     QString            companyId;            /**< Unique ID of the tablet company (as hex)*/
00044     QString            deviceId;             /**< Unique device ID (as hex)*/
00045     QString            companyName;          /**< Name of the tablet company */
00046     QString            deviceName;           /**< Name of the tablet */
00047     QString            deviceModel;          /**< Model name of the tablet */
00048     QStringList        deviceList;           /**< List of all internal input device names found via xinput --list */
00049     QString            internalPadName;      /**< Internal name of the pad */
00050     QString            internalStylusName;   /**< Internal name of the stylus */
00051     QString            internalEraserName;   /**< Internal name of the eraser */
00052     QString            internalCursorName;   /**< Internal name of the cursor */
00053     QString            internalTouchName;    /**< Internal name of the touch device */
00054     bool               isDeviceAvailable;    /**< Is a tabled device connected or not? */
00055     bool               hasPadButtons;        /**< Does the tablet device has buttons that can be configured? */
};
}

using namespace Wacom;

00061 DeviceHandler::DeviceHandler()
        : d_ptr(new DeviceHandlerPrivate)
{
    Q_D(DeviceHandler);
    d->curDevice = 0;
    d->isDeviceAvailable = false;
    d->companyConfig = KSharedConfig::openConfig(KStandardDirs::locate("data", "kcmtablet/data/companylist"), KConfig::SimpleConfig, "data");

    if (d->companyConfig->groupList().isEmpty()) {
        kError() << "company list missing";
    }

}

00075 DeviceHandler::~DeviceHandler()
{
    delete this->d_ptr->curDevice;
    delete this->d_ptr;
}

00081 bool DeviceHandler::detectTablet()
{
    // start with xinput --list regardless of the device detection
    // xinput must list some input devices or the tablet is not installed correctly
    if (!findXInputDevice()) {
        if (!findWacomDevList()) {
            kDebug() << "no input devices (pad/stylus/eraser/cursor/touch) found via xinput --list and xsetwacom list dev";

            return false;
        }
    }

    // ok seems there is at least something connected, now try to get more information about the tablet device

    // first check lsusb
    if (!findUSBDevice()) {
        kDebug() << "no usb device found via lsusb :: check serial device";

        if (!findSerialDevice()) {
            kDebug() << "no serial device found";

            return false;
        }
    }

    Q_D(DeviceHandler);

    // ok up to this point we have a tablet found somehow lets set it up to work with it
    if (!setDeviceInformation(d->companyId, d->deviceId)) {
        kError() << "could not set up the tablet information";
        return false;
    }

    // \0/
    d->isDeviceAvailable = true;
    return true;
}

00119 void DeviceHandler::reloadDeviceInformation()
{
    Q_D(DeviceHandler);

    //reset all values
    d->companyId.clear();
    d->deviceId.clear();
    d->companyName.clear();
    d->deviceName.clear();
    d->deviceModel.clear();
    d->deviceList.clear();
    d->internalPadName.clear();
    d->internalStylusName.clear();
    d->internalEraserName.clear();
    d->internalCursorName.clear();
    d->internalTouchName.clear();
    delete d->curDevice;
    d->curDevice = 0;
    d->isDeviceAvailable = false;
    d->hasPadButtons = false;

    detectTablet();
}

00143 bool DeviceHandler::findUSBDevice()
{
    // read lsusb
    QProcess lsusb;
    lsusb.start("lsusb");
    lsusb.waitForStarted();
    lsusb.waitForFinished();

    while (!lsusb.atEnd()) {
        QRegExp rxlen("ID\\s(.{4}):(.{4})");
        int pos = rxlen.indexIn(lsusb.readLine());
        if (pos > -1) {
            if (detectDeviceInformation(rxlen.cap(1), rxlen.cap(2))) {
                return true;
            }
        }
    }

    return false;
}

00164 bool DeviceHandler::findSerialDevice()
{
    Q_D(DeviceHandler);

    // ok up to this point we know there are some input devices connected and found via xinput
    // and the devices are not detected by lsusb

    // ok first we take the internal pad name and ask xsetwacom for a tablet ID
    // we might get lucky and detect a serial device somehow
    QString nameUsedForDetection;
    if (d->internalPadName.isEmpty()) {
        nameUsedForDetection = d->internalStylusName;
    } else {
        nameUsedForDetection = d->internalPadName;
    }

    QString cmd = QString("xsetwacom get \"%1\" TabletID").arg(nameUsedForDetection);

    QProcess wacomToolID;
    wacomToolID.start(cmd);
    wacomToolID.waitForStarted();
    wacomToolID.waitForFinished();

    // well it does not return anything if there is an error
    //@TODO need to check if this way to detect a tablet id works in general or just with my kubuntu setup
    QString result = QString(wacomToolID.readAll());
    result.remove('\n');

    // ok seems we have a tablet that can be handled with the wacom tools
    // see if we can find it in our config files to get more information
    if (!result.isEmpty()) {

        //transform tablet number into hex number (somehow .toInt(&ok,16) does not work
        int tableIntID = result.toInt();
        QString tabletHexID = QString::number(tableIntID, 16);
        for (int i = tabletHexID.count(); i < 4; i++) {
            tabletHexID.prepend("0");
        }

        // go through all known company's in the company list and see what we can find
        //@BUG this is definitely a problem if different company's use the same hexnumber to identify the tablet
        foreach(const QString &tempCompID, d->companyConfig->groupList()) {
            if (detectDeviceInformation(tempCompID, tabletHexID)) {
                kDebug() << "tablet ID" << tabletHexID << "found!";
                return true;
            }
        }

        // ok device was not found but there seems to be some kind of tablet otherwise the "result" string is empty
        // lets define the company as unknown and the device as default and hope it helps
        kError() << "found some device but no clue what the device (" << tabletHexID << ") is ... use default config for a wacom-tool tablet";
        d->companyId = QString("wacom-default");
        d->deviceId = QString("wacom-default");
        return true;
    } else {
        kError() << "xsetwacom get \"pad\" TabletID returned no information ... seems the wacom-tools are not installed or don't support your tablet";
    }

    // to bad could still not identify the tablet
    return false;
}

00226 bool DeviceHandler::findXInputDevice()
{
    Q_D(DeviceHandler);

    d->deviceList.clear();

    // read xinput --list
    QProcess xinput;
    xinput.start("xinput --list");
    xinput.waitForStarted();
    xinput.waitForFinished();


    //@TODO find better parsing rules
    QRegExp rxWacom("\"((.*wacom.*)|(.*waltop.*)|(stylus|pad|eraser|cursor|touch))\"", Qt::CaseInsensitive);
    // this is a rather bad hack to identify some devices that are a stylus/eraser and so on but named differently
    // got the xinput --list results that showed "Type is Wacom stylus" and so on in the line after the device name
    // this line does not show up in my xinput list ... thus this might only work for one person at all
    QRegExp rxTypeCheck("Type is Wacom (stylus|pad|eraser|cursor|touch)", Qt::CaseInsensitive);
    QRegExp rxTypeCheckLastName("\"(.*)\"", Qt::CaseInsensitive);
    QString curLine;
    QString lastLine;

    // parse xinput
    while (!xinput.atEnd()) {
        lastLine = curLine;
        curLine = xinput.readLine();

        int pos = rxWacom.indexIn(curLine);
        if (pos > -1) {
            QString deviceName = rxWacom.cap(1);

            //add device name to the list of known devices
            d->deviceList.append(deviceName);

            //figure out what device we just found and save it as reference

            if (deviceName.contains("pad")) {
                d->internalPadName = deviceName;
            } else if (deviceName.contains("eraser")) {
                d->internalEraserName = deviceName;
            } else if (deviceName.contains("cursor")) {
                d->internalCursorName = deviceName;
            } else if (deviceName.contains("touch")) {
                d->internalTouchName = deviceName;
            } else if (deviceName.contains("stylus")) {
                d->internalStylusName = deviceName;
            } else {
                d->internalStylusName = deviceName; //assume stylus if no information is given, this is how Kubuntu HAL config handles it (my stylus is called "Wacom Bamboo")
            }
        }
        // if we can't find the device name normally,check if the next line contains "Type is Wacom xxx"
        // this is more of a hack than a good solution ...
        else {
            int posAlternate = rxTypeCheck.indexIn(curLine);

            if (posAlternate > -1) {
                QString deviceType = rxTypeCheck.cap(1);

                // next line fits what we need, so lets get the name in the quotes from the line before
                int posAlternateName = rxTypeCheckLastName.indexIn(lastLine);

                if (posAlternateName > -1) {
                    QString deviceName = rxTypeCheckLastName.cap(1);

                    //add device name to the list of known devices
                    d->deviceList.append(deviceName);

                    //check what type we found (with Type is Wacom xxx) and save internal reference)
                    if (deviceType.contains("pad", Qt::CaseInsensitive)) {
                        d->internalPadName = deviceName;
                    } else if (deviceType.contains("eraser", Qt::CaseInsensitive)) {
                        d->internalEraserName = deviceName;
                    } else if (deviceType.contains("cursor", Qt::CaseInsensitive)) {
                        d->internalCursorName = deviceName;
                    } else if (deviceType.contains("touch",  Qt::CaseInsensitive)) {
                        d->internalTouchName = deviceName;
                    } else if (deviceType.contains("stylus", Qt::CaseInsensitive)) {
                        d->internalStylusName = deviceName;
                    }
                }
            }
        }
    }

    // Regexp could not find any device named stylus/pad/eraser/cursor/touch or with wacom in the name
    if (d->deviceList.isEmpty()) {
        return false;
    }

    // This happens because we deal with two different output results of xinput --list
    // happens that both fit ... really time to let solid handle this
    d->deviceList.removeDuplicates();

    // if we just have no pad name only a name for a stylus...copy that, this is how the touchpc devices seem to work
    if (d->internalPadName.isEmpty()) {
        d->internalPadName = d->internalStylusName;
    }

    // seems we found something useful
    return true;
}

00329 bool DeviceHandler::findWacomDevList()
{
    Q_D(DeviceHandler);
    d->deviceList.clear();

    // read xinput --list
    QProcess xinput;
    xinput.start("xsetwacom list dev");
    xinput.waitForStarted();
    xinput.waitForFinished();

    QRegExp rxStylus("(.*) STYLUS", Qt::CaseInsensitive);
    QRegExp rxPad("(.*) PAD", Qt::CaseInsensitive);
    QRegExp rxCursor("(.*) CURSOR", Qt::CaseInsensitive);
    QRegExp rxEraser("(.*) ERASER", Qt::CaseInsensitive);
    QString curLine;

    // parse xinput
    while (!xinput.atEnd()) {
        curLine = xinput.readLine();

        int pos = rxStylus.indexIn(curLine);
        if (pos > -1) {
            QString deviceName = rxStylus.cap(1);

            //add device name to the list of known devices
            d->deviceList.append(deviceName);
            d->internalStylusName = deviceName;
        }
        pos = rxPad.indexIn(curLine);
        if (pos > -1) {
            QString deviceName = rxPad.cap(1);

            //add device name to the list of known devices
            d->deviceList.append(deviceName);
            d->internalPadName = deviceName;
        }
        pos = rxCursor.indexIn(curLine);
        if (pos > -1) {
            QString deviceName = rxCursor.cap(1);

            //add device name to the list of known devices
            d->deviceList.append(deviceName);
            d->internalCursorName = deviceName;
        }
        pos = rxEraser.indexIn(curLine);
        if (pos > -1) {
            QString deviceName = rxEraser.cap(1);

            //add device name to the list of known devices
            d->deviceList.append(deviceName);
            d->internalEraserName = deviceName;
        }
    }

    // Regexp could not find any device named stylus/pad/eraser/cursor/touch or with wacom in the name
    if (d->deviceList.isEmpty()) {
        return false;
    }

    // This happens because we deal with two different output results of xinput --list
    // happens that both fit ... really time to let solid handle this
    d->deviceList.removeDuplicates();

    // if we just have no pad name only a name for a stylus...copy that, this is how the touchpc devices seem to work
    if (d->internalPadName.isEmpty()) {
        d->internalPadName = d->internalStylusName;
    }

    // seems we found something useful
    return true;
}

00402 bool DeviceHandler::detectDeviceInformation(const QString & companyId, const QString & deviceId)
{
    Q_D(DeviceHandler);
    KConfigGroup companyGroup = KConfigGroup(d->companyConfig, companyId);

    if (companyGroup.keyList().isEmpty()) {
        //kDebug() << "no company information found for ID: " << companyId;
        return false;
    }

    // read the company name and the datafile for the device information
    KSharedConfig::Ptr deviceConfig = KSharedConfig::openConfig(KStandardDirs::locate("data", QString("kcmtablet/data/%1").arg(companyGroup.readEntry("listfile"))), KConfig::SimpleConfig, "data");

    if (deviceConfig->groupList().isEmpty()) {
        kError() << "device list missing for company ID: " << companyId;
        return false;
    }

    KConfigGroup deviceGroup = KConfigGroup(deviceConfig, deviceId.toUpper());

    if (deviceGroup.keyList().isEmpty()) {
        kDebug() << "device info not found for device ID: " << deviceId << " :: company" << companyGroup.readEntry("name");
        return false;
    }

    // ok we found a list to the corresponding company id and a device that fits
    d->deviceId = deviceId.toUpper();
    d->companyId = companyId;

    return true;
}

00434 bool DeviceHandler::setDeviceInformation(const QString & companyId, const QString & deviceId)
{
    Q_D(DeviceHandler);
    KConfigGroup companyGroup = KConfigGroup(d->companyConfig, companyId);

    // read the company name and the datafile for the device information
    KSharedConfig::Ptr deviceConfig = KSharedConfig::openConfig(KStandardDirs::locate("data", QString("kcmtablet/data/%1").arg(companyGroup.readEntry("listfile"))), KConfig::SimpleConfig, "data");

    KConfigGroup deviceGroup = KConfigGroup(deviceConfig, deviceId.toUpper());

    d->companyName = companyGroup.readEntry("name");
    d->deviceModel = deviceGroup.readEntry("model");
    d->deviceName = deviceGroup.readEntry("name");

    if (deviceGroup.readEntry("padbuttons")  != QString('0') ||
            deviceGroup.readEntry("wheel")       != QString("no") ||
            deviceGroup.readEntry("touchring")   != QString("no") ||
            deviceGroup.readEntry("touchstripl") != QString("no") ||
            deviceGroup.readEntry("touchstripr") != QString("no")) {
        d->hasPadButtons = true;
    } else {
        d->hasPadButtons = false;
    }

    selectDeviceBackend(companyGroup.readEntry("driver"));
    return true;
}

00462 void DeviceHandler::selectDeviceBackend(const QString & backendName)
{
    Q_D(DeviceHandler);
    //@TODO add switch statement to handle other backends too
    if (backendName == QString("wacom-tools")) {
        d->curDevice = new WacomInterface();
    }

    if (!d->curDevice) {
        kError() << "unknown device backend!" << backendName;
    }
}

bool DeviceHandler::isDeviceAvailable() const
{
    Q_D(const DeviceHandler);
    return d->isDeviceAvailable;
}

bool DeviceHandler::hasPadButtons() const
{
    Q_D(const DeviceHandler);
    return d->hasPadButtons;
}

00487 QString DeviceHandler::deviceId() const
{
    Q_D(const DeviceHandler);
    return d->deviceId;
}

00493 QString DeviceHandler::companyId() const
{
    Q_D(const DeviceHandler);
    return d->companyId;
}

QString DeviceHandler::companyName() const
{
    Q_D(const DeviceHandler);
    return d->companyName;
}

QString DeviceHandler::deviceName() const
{
    Q_D(const DeviceHandler);
    return d->deviceName;
}

QString DeviceHandler::deviceModel() const
{
    Q_D(const DeviceHandler);
    return d->deviceModel;
}

QStringList DeviceHandler::deviceList() const
{
    Q_D(const DeviceHandler);
    return d->deviceList;
}

QString DeviceHandler::padName() const
{
    Q_D(const DeviceHandler);
    // if no pad is present, use stylus name as alternative way
    // fixes some problems with serial TabletPC that do not have a pad as such but still
    // can handle pad rotations and such when applied to the stylus settings
    if (d->internalPadName.isEmpty()) {
        return d->internalCursorName;
    } else {
        return d->internalPadName;
    }
}

QString DeviceHandler::stylusName() const
{
    Q_D(const DeviceHandler);
    return d->internalStylusName;
}

QString DeviceHandler::eraserName() const
{
    Q_D(const DeviceHandler);
    return d->internalEraserName;
}

QString DeviceHandler::cursorName() const
{
    Q_D(const DeviceHandler);
    return d->internalCursorName;
}

QString DeviceHandler::touchName() const
{
    Q_D(const DeviceHandler);
    return d->internalCursorName;
}

00560 QString DeviceHandler::name(const QString & name) const
{
    Q_D(const DeviceHandler);
    if (name.contains("pad")) {
        return d->internalPadName;
    }
    if (name.contains("stylus")) {
        return d->internalStylusName;
    }
    if (name.contains("erser")) {
        return d->internalEraserName;
    }
    if (name.contains("cursor")) {
        return d->internalCursorName;
    }
    if (name.contains("touch")) {
        return d->internalTouchName;
    }

    return QString();
}

00582 void DeviceHandler::applyProfile(KConfigGroup *gtprofile)
{
    Q_D(DeviceHandler);
    d->curDevice->applyProfile(d->internalPadName, "pad", gtprofile);
    d->curDevice->applyProfile(d->internalStylusName, "stylus", gtprofile);
    d->curDevice->applyProfile(d->internalEraserName, "eraser", gtprofile);
    //d->curDevice->applyProfile(d->internalCursorName, "cursor", gtprofile);
}

00591 void DeviceHandler::setConfiguration(const QString & device, const QString & param, const QString & value)
{
    Q_D(DeviceHandler);
    d->curDevice->setConfiguration(device, param, value);
}

00597 QString DeviceHandler::getConfiguration(const QString & device, const QString & param) const
{
    Q_D(const DeviceHandler);
    return d->curDevice->getConfiguration(device, param);
}

00603 QString DeviceHandler::getDefaultConfiguration(const QString & device, const QString & param) const
{
    Q_D(const DeviceHandler);
    return d->curDevice->getDefaultConfiguration(device, param);
}

Generated by  Doxygen 1.6.0   Back to index