Logo Search packages:      
Sourcecode: synaptiks version File versions  Download package

qxdevice.cpp

/*
 * synaptiks -- a touchpad control tool
 *
 *
 * Copyright (C) 2009, 2010 Sebastian Wiesner <basti.wiesner@gmx.net>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "qxdevice.h"
#ifdef HAVE_XINPUT2
#include "qxinput2.h"
#else /* HAVE_XINPUT2 */
#include "qxinput.h"
#endif /* HAVE_XINPUT2 */
#include <QtCore/QPair>
#include <QtCore/QSharedPointer>
#include <QtGui/QX11Info>
extern "C" {
#include <X11/Xatom.h>
}
#include <algorithm>


00045 QXDeviceError::QXDeviceError(const QByteArray &name,
                             const QString &message):
    m_name(name), m_message(message) {
}

QXDeviceError::~QXDeviceError() {}

00052 QByteArray QXDeviceError::name() const {
    return this->m_name;
}

00056 QString QXDeviceError::message() const {
    return this->m_message;
}

00060 QString QXDeviceError::toString() const {
    return i18nc("device error",
                 "%1: %2", QString(this->name()), this->message());
}

00065 QXDevicePropertyError::QXDevicePropertyError(const QByteArray &name,
                                             const QByteArray &property,
                                             const QString &message):
    QXDeviceError(name, message), m_property(property) {}

QXDevicePropertyError::~QXDevicePropertyError() {}

00072 QByteArray QXDevicePropertyError::property() const {
    return this->m_property;
}

00076 QString QXDevicePropertyError::toString() const {
    return i18nc("device error with property name",
                 "%1: %2: %3", QString(this->name()),
                 QString(this->property()), this->message());
}


00083 QXNoSuchPropertyError::QXNoSuchPropertyError(const QByteArray &name,
                                             const QByteArray &property):
    QXDevicePropertyError(name, property, i18nc("device error message",
                                                "no such property")) {}


static const char ATOM_FLOAT[] = "FLOAT";


#ifdef HAVE_XINPUT2
typedef quint8 qx_byte_t;
typedef quint16 qx_short_t;
typedef quint32 qx_word_t;

class QXDevicePrivate {
public:
    int device;
    QByteArray name;
};
#else /* HAVE_XINPUT2 */
/** Praise xlib for its broken 64-bit property handling! */
typedef char qx_byte_t;
typedef short qx_short_t;
typedef long qx_word_t;

00108 class QXDevicePrivate {
public:
    QSharedPointer<XDevice> device;
    QByteArray name;

    QXDevicePrivate(): device(0, QX11::CloseDevice) {}

};
#endif /* HAVE_XINPUT2 */

00118 union qx_float_t {
    qx_word_t i;
    float value;
};


#if !defined(KDE_NO_DEBUG_OUTPUT)
QDebug operator<<(QDebug dbg, const QList<QByteArray> &c) {
    dbg.nospace() << "(";
    for (int i=0; i < c.length(); i++) {
        dbg <<  c.at(i).toHex();
        if (i != c.length() - 1)
            dbg << ", ";
    }
    dbg << ")";
    return dbg.space();
}
#endif

/**
 * Convert a property type to the corresponding X Atom.
 */
static inline Atom atomForPropertyType(QXDevice::PropertyType type) {
    switch (type) {
    case QXDevice::IntegerType:
        return XA_INTEGER;
    case QXDevice::FloatType:
        return QX11::InternAtom(ATOM_FLOAT, true);
    default:
        // invalid atom
        return 0;
    }
}

static inline int bytesPerItem(QXDevice::PropertyFormat format) {
    switch (format) {
    case QXDevice::ByteFormat:
        return sizeof(qx_byte_t);
    case QXDevice::ShortFormat:
        return sizeof(qx_short_t);
    case QXDevice::WordFormat:
        return sizeof(qx_word_t);
    default:
        // should never happen
        kFatal() << "invalid format" << format;
        return -1;
    }
}

/**
 * Check, whether XInput is available and recent enough for device
 * properties.
 *
 * @return @c true, if device properties are supported, @c false otherwise
 */
static inline bool haveXInputProperties() {
#ifdef HAVE_XINPUT2
    int major = 2;
    int minor = 0;
    return QX11::XI::QueryVersion(&major, &minor) == Success;
#else /* HAVE_XINPUT2 */
    QSharedPointer<XExtensionVersion> version(
        QX11::GetExtensionVersion("XInputExtension"), XFree);
    QPair<int, int> current(version->major_version, version->minor_version);
    QPair<int, int> required(XI_Add_DeviceProperties_Major,
                             XI_Add_DeviceProperties_Minor);
    return version->present && (current >= required);
#endif /* HAVE_XINPUT2 */
}

00188 bool QXDevice::isSupported() {
    if (!QX11Info::display()) {
        kWarning() << "no display available!";
        return false;
    }
    if (!haveXInputProperties()) {
        kDebug() << "device properties not supported,"
                 << "XInput not available or too old";
        return false;
    }
    return true;
}

00201 bool QXDevice::isPropertyDefined(const QByteArray &name) {
    return QX11::InternAtom(name, true);
}

00205 QXDevice::List QXDevice::findDevicesWithProperty(
    const QByteArray &property) {
    QXDevice::List devices;
    kDebug() << "scanning input devices";
    int ndevices = 0;
#ifdef HAVE_XINPUT2
    QSharedPointer<XIDeviceInfo> deviceinfo(
        QX11::XI::QueryDevice(XIAllDevices, &ndevices),
        XIFreeDeviceInfo);
#else /* HAVE_XINPUT2 */
    QSharedPointer<XDeviceInfo> deviceinfo(
        QX11::ListInputDevices(&ndevices), XFreeDeviceList);
#endif /* HAVE_XINPUT2 */
    while (ndevices--) {
        QByteArray name = deviceinfo.data()[ndevices].name;
#ifdef HAVE_XINPUT2
        ID id = deviceinfo.data()[ndevices].deviceid;
#else /* HAVE_XINPUT2 */
        ID id = deviceinfo.data()[ndevices].id;
#endif /* HAVE_XINPUT2 */
        kDebug() << "opening device" << name;
        try {
            QSharedPointer<QXDevice> device(new QXDevice(name, id));
            kDebug() << "checking device" << name << "for property" << property;
            if (!device->hasProperty(property)) {
                continue;
            }
            kDebug() << "found device" << name;
            devices.append(device);
        } catch (const QXDeviceError &error) {
            kDebug() << "device" << name << "could not be used, "
                     << "discarding";
            continue;
        }
    }
    return devices;
}

00243 QXDevice::QXDevice(const QByteArray &name, const ID id):
    d_ptr(new QXDevicePrivate) {
    Q_D(QXDevice);
    d->name = name;
#ifdef HAVE_XINPUT2
    d->device = id;
#else /* HAVE_XINPUT2 */
    d->device = QSharedPointer<XDevice>(QX11::OpenDevice(id),
                                        QX11::CloseDevice);
    if (!d->device) {
        // error out
        delete this->d_ptr;
        throw QXDeviceError(name, i18nc("device error message",
                                        "invalid device"));
    }
#endif /* HAVE_XINPUT2 */
}

00261 QXDevice::~QXDevice() {
    delete this->d_ptr;
}

00265 QByteArray QXDevice::name() const {
    Q_D(const QXDevice);
    return d->name;
}

00270 bool QXDevice::hasProperty(const QByteArray &name) const {
    Q_D(const QXDevice);
    Atom property = QX11::InternAtom(name, true);
    if (!property) {
        return false;
    }
    int nprops;
#ifdef HAVE_XINPUT2
    QSharedPointer<Atom> properties(
        QX11::XI::ListProperties(d->device, &nprops), XFree);
#else /* HAVE_XINPUT2 */
    QSharedPointer<Atom> properties(
        QX11::ListDeviceProperties(d->device.data(), &nprops), XFree);
#endif /* HAVE_XINPUT2 */
    while (nprops--) {
        if (properties.data()[nprops] == property) {
            return true;
        }
    }
    return false;
}

00292 QList<QByteArray> QXDevice::property(const QByteArray &name,
                                     PropertyType type,
                                     PropertyFormat format) const {
    Q_D(const QXDevice);
    Atom property = QX11::InternAtom(name, true);
    if (!property) {
        throw QXNoSuchPropertyError(this->name(), name);
    }

    Atom typeAtom = atomForPropertyType(type);
    if (!typeAtom) {
        throw QXDevicePropertyError(
            this->name(), name, i18nc("device error message",
                                      "unknown property type"));
    }

    QList<QByteArray> values;
    // these are set to the actual return type and the format of the
    // returned property
    Atom returnType;
    int returnFormat;
    // this is set to the number of data items in the return value (which is
    // *NOT* necessarily equal to the number of bytes returned)
    unsigned long nitems;
    // this is set to the amount of bytes, that where not read from the end
    // of the property data
    unsigned long bytesAfter;
    // this holds the amount of 32-bit words, which shall be returned from
    // the property
    long length = 1;
    // this stores the number of bytes a single data item contains
    int itemBytes = bytesPerItem(format);
    // data pointer
    QSharedPointer<unsigned char> data(0, XFree);

    // loop until zero bytes are left (bytesAfter == 0) to make sure, that
    // all property items are retrieved.
    do {
        // this is set to point to the actual data array
        unsigned char *dataptr = 0;

#ifdef HAVE_XINPUT2
        int error = QX11::XI::GetProperty(
            d->device, property, 0, length, false, typeAtom, &returnType,
            &returnFormat, &nitems, &bytesAfter, &dataptr);
#else /* HAVE_XINPUT2 */
        int error = QX11::GetDeviceProperty(
            d->device.data(), property, 0, length, false, typeAtom,
            &returnType, &returnFormat, &nitems, &bytesAfter, &dataptr);
#endif /* HAVE_XINPUT2 */
        data = QSharedPointer<unsigned char>(dataptr, XFree);

        if (error) {
            throw QXDevicePropertyError(
                this->name(), name, i18nc("device error message",
                                          "could not query property"));
        } else if (returnType != typeAtom) {
            throw QXDevicePropertyError(
                this->name(), name,
                i18n("unexpected property type %1",
                     QString(QX11::GetAtomName(returnType))));
        } else if (returnFormat != format) {
            throw QXDevicePropertyError(
                this->name(), name, i18nc("device error message",
                                          "unexpected property format %1",
                                          returnFormat));
        }
        length++;
    } while (bytesAfter);

    // extract each property item into a separate byte array
    // "current" points to the beginning of a data item
    unsigned char *current = data.data();
    for (unsigned long i=0; i < nitems; i++) {
        QByteArray item(reinterpret_cast<char *>(current), itemBytes);
        Q_ASSERT(item.length() == itemBytes);
        values << item;
        // move to the next data item
        current += itemBytes;
    }
    kDebug() << "received" << values << "for" << name;
    return values;
}

00376 void QXDevice::setProperty(const QByteArray &name, PropertyType type,
                           PropertyFormat format,
                           const QList<QByteArray> &values) {
    Q_D(QXDevice);

    Atom property = QX11::InternAtom(name, true);
    if (!property) {
        throw QXNoSuchPropertyError(this->name(), name);
    }

    Atom typeAtom = atomForPropertyType(type);
    if (!typeAtom) {
        throw QXDevicePropertyError(
            this->name(), name, i18nc("device error message",
                                      "unknown property type"));
    }

    // the number of bytes required for a single item
    int itemBytes = bytesPerItem(format);
    // initialize the data vector
    QVector<unsigned char> data(values.length() * itemBytes);
    // copy all items into the data vector
    QVector<unsigned char>::iterator current = data.begin();
    foreach (const QByteArray &value, values) {
        Q_ASSERT(value.length() == itemBytes);
        current = std::copy(value.data(), value.data()+value.length(),
                            current);
    }
    kDebug() << "setting" << values << "for" << name;
#ifdef HAVE_XINPUT2
    QX11::XI::ChangeProperty(d->device, property, typeAtom, format,
                             PropModeReplace, data.data(), values.size());
#else /* HAVE_XINPUT2 */
    QX11::ChangeDeviceProperty(d->device.data(), property, typeAtom,
                               format, PropModeReplace, data.data(),
                               values.size());
#endif /* HAVE_XINPUT2 */
}

/**
 * @brief Get a property as list of boolean values.
 */
template<>
00419 QList<bool> QXDevice::property<bool>(const QByteArray &name) const {
    QList<bool> values;
    QList<QByteArray> data = this->property(name, IntegerType, ByteFormat);
    foreach (const QByteArray &item, data) {
        values << static_cast<bool>(item.at(0));
    }
    return values;
}

/**
 * @brief Set a boolean property.
 */
template <>
00432 void QXDevice::setProperty<bool>(const QByteArray &name,
                                 const QList<bool> &values) {
    QList<QByteArray> data;
    foreach (bool value, values) {
        QByteArray item;
        item.append(value);
        data << item;
    }
    this->setProperty(name, IntegerType, ByteFormat, data);
}

/**
 * @brief Get a property as list of single bytes.
 */
template<>
QList<uchar> QXDevice::property<uchar>(const QByteArray &name) const {
    QList<uchar> values;
    QList<QByteArray> data = this->property(name, IntegerType, ByteFormat);
    foreach (const QByteArray &item, data) {
        values << item.at(0);
    }
    return values;
}

/**
 * @brief Set a property as byte list.
 */
template<>
00460 void QXDevice::setProperty<uchar>(const QByteArray &name,
                                  const QList<uchar> &values) {
    QList<QByteArray> data;
    foreach (char value, values) {
        data << QByteArray(&value, sizeof(qx_byte_t));
    }
    this->setProperty(name, IntegerType, ByteFormat, data);
}

/**
 * @brief Get a property as a list of @c int integers.
 */
template<>
QList<int> QXDevice::property<int>(const QByteArray &name) const {
    QList<int> values;
    QList<QByteArray> data = this->property(name, IntegerType, WordFormat);
    foreach (const QByteArray &item, data) {
        values << *(reinterpret_cast<const qx_word_t *>(item.data()));
    }
    return values;
}

/**
 * @brief Set a property as list of @c int integers.
 */
template<>
00486 void QXDevice::setProperty<int>(const QByteArray &name,
                                 const QList<int> &values) {
    QList<QByteArray> data;
    foreach (int value, values) {
        qx_word_t word_value = static_cast<qx_word_t>(value);
        data << QByteArray(reinterpret_cast<char *>(&word_value),
                           sizeof(qx_word_t));
    }
    this->setProperty(name, IntegerType, WordFormat, data);
}

/**
 * @brief Get a property as a list of @c float values.
 */
template<>
QList<float> QXDevice::property<float>(const QByteArray &name) const {
    QList<float> values;
    QList<QByteArray> data = this->property(name, FloatType, WordFormat);
    foreach (const QByteArray &item, data) {
        // ugly x86_64-specific hack to correctly decode 32 bit floats
        // returned in 64 bit items
        const union qx_float_t *data_item =
            reinterpret_cast<const union qx_float_t *>(item.data());
        values << (*data_item).value;
    }
    return values;
}

/**
 * @brief Set a property as list of @c float values.
 */
template<>
00518 void QXDevice::setProperty<float>(const QByteArray &name,
                                  const QList<float> &values) {
    QList<QByteArray> data;
    // same 64-bit hack from property<float> applied in reverse direction
    union qx_float_t item;
    foreach (float value, values) {
        item.value = value;
        data << QByteArray(reinterpret_cast<char *>(&item),
                           sizeof(union qx_float_t));
    }
    this->setProperty(name, FloatType, WordFormat, data);
}

Generated by  Doxygen 1.6.0   Back to index