Reorganize evdev plugins

linuxinput becomes evdevmouse. The experimental touch code is removed,
now the plugin's purpose is solely to generate mouse events from
absolute and relative pointer events.  The plugin key is EvdevMouse.

touchscreen becomes evdevtouch. The plugin key is EvdevTouch.

In case keyboard support appears some day, it will fit nicely in the
system by the name of evdevkeyboard or similar.

Some little udev code is moved to platformsupport so it can be shared
between the plugins. This may be extended later if more sophisticated
udev support is needed. N.B. the intention is to keep this as simple
as possible. We are shipping these plug-ins as reference examples, not
as full-featured drivers.

evdev and udev support has configure time tests from now on. This
means the "drivers" (generic plugins) will get built automatically
when the support is available.

Change-Id: Iaf6260b5c2edfb9f25d070d2764466725adc6b4e
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@nokia.com>
Reviewed-by: Samuel Rødal <samuel.rodal@nokia.com>
This commit is contained in:
Laszlo Agocs 2012-02-05 13:32:40 +02:00 committed by Qt by Nokia
parent 009cd671ec
commit 684a1559f0
26 changed files with 651 additions and 692 deletions

View File

@ -0,0 +1,59 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the config.tests of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <linux/input.h>
enum {
e1 = ABS_MT_POSITION_X,
e2 = ABS_MT_TRACKING_ID,
e3 = ABS_PRESSURE,
e4 = ABS_X,
e5 = REL_X,
e6 = SYN_REPORT,
e7 = SYN_MT_REPORT
};
int main()
{
::input_event buf[32];
(void) buf;
return 0;
}

View File

@ -0,0 +1,2 @@
SOURCES = evdev.cpp
CONFIG -= qt

View File

@ -0,0 +1,49 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the config.tests of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <libudev.h>
int main()
{
udev *u = udev_new();
udev_unref(u);
return 0;
}

View File

@ -0,0 +1,3 @@
SOURCES = libudev.cpp
CONFIG -= qt
LIBS += -ludev

42
configure vendored
View File

@ -727,6 +727,8 @@ CFG_XKB=auto
CFG_XCB=auto CFG_XCB=auto
CFG_XCB_LIMITED=yes CFG_XCB_LIMITED=yes
CFG_WAYLAND=auto CFG_WAYLAND=auto
CFG_LIBUDEV=auto
CFG_EVDEV=auto
CFG_NIS=auto CFG_NIS=auto
CFG_CUPS=auto CFG_CUPS=auto
CFG_ICONV=auto CFG_ICONV=auto
@ -1843,6 +1845,20 @@ while [ "$#" -gt 0 ]; do
UNKNOWN_OPT=yes UNKNOWN_OPT=yes
fi fi
;; ;;
libudev)
if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then
CFG_LIBUDEV="$VAL"
else
UNKNOWN_OPT=yes
fi
;;
evdev)
if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then
CFG_EVDEV="$VAL"
else
UNKNOWN_OPT=yes
fi
;;
cups) cups)
if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then
CFG_CUPS="$VAL" CFG_CUPS="$VAL"
@ -5651,6 +5667,32 @@ if [ "$PLATFORM_QPA" = "yes" ]; then
fi fi
fi fi
if [ "$CFG_LIBUDEV" != "no" ]; then
if "$unixtests/compile.test" "$XQMAKESPEC" "$QMAKE_CONFIG" $OPT_VERBOSE "$relpath" "$outpath" config.tests/unix/libudev "libudev" $L_FLAGS $I_FLAGS $l_FLAGS; then
CFG_LIBUDEV=yes
QT_CONFIG="$QT_CONFIG libudev"
elif [ "$CFG_LIBUDEV" = "yes" ]; then
echo "The libudev functionality test failed!"
exit 1
else
CFG_LIBUDEV=no
QMakeVar add DEFINES QT_NO_LIBUDEV
fi
fi
if [ "$CFG_EVDEV" != "no" ]; then
if "$unixtests/compile.test" "$XQMAKESPEC" "$QMAKE_CONFIG" $OPT_VERBOSE "$relpath" "$outpath" config.tests/unix/evdev "evdev" $L_FLAGS $I_FLAGS $l_FLAGS; then
CFG_EVDEV=yes
QT_CONFIG="$QT_CONFIG evdev"
elif [ "$CFG_EVDEV" = "yes" ]; then
echo "The evdev functionality test failed!"
exit 1
else
CFG_EVDEV=no
QMakeVar add DEFINES QT_NO_EVDEV
fi
fi
# Check we actually have X11 :-) # Check we actually have X11 :-)
if "$unixtests/compile.test" "$XQMAKESPEC" "$QMAKE_CONFIG" $OPT_VERBOSE "$relpath" "$outpath" config.tests/x11/xlib "XLib" $L_FLAGS $I_FLAGS $l_FLAGS $X11TESTS_FLAGS; then if "$unixtests/compile.test" "$XQMAKESPEC" "$QMAKE_CONFIG" $OPT_VERBOSE "$relpath" "$outpath" config.tests/x11/xlib "XLib" $L_FLAGS $I_FLAGS $l_FLAGS $X11TESTS_FLAGS; then
QT_CONFIG="$QT_CONFIG xlib" QT_CONFIG="$QT_CONFIG xlib"

View File

@ -33,3 +33,4 @@ include(fontdatabases/fontdatabases.pri)
include(glxconvenience/glxconvenience.pri) include(glxconvenience/glxconvenience.pri)
#include(printersupport/printersupport.pri) #include(printersupport/printersupport.pri)
include(inputcontext/inputcontext.pri) include(inputcontext/inputcontext.pri)
include(udev/udev.pri)

View File

@ -0,0 +1,73 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qudevhelper_p.h"
#include <libudev.h>
QT_BEGIN_NAMESPACE
void q_udev_devicePath(int type, QString *path)
{
*path = QString();
udev *u = udev_new();
udev_enumerate *ue = udev_enumerate_new(u);
udev_enumerate_add_match_subsystem(ue, "input");
if (type & UDev_Mouse)
udev_enumerate_add_match_property(ue, "ID_INPUT_MOUSE", "1");
if (type & UDev_Touchpad)
udev_enumerate_add_match_property(ue, "ID_INPUT_TOUCHPAD", "1");
if (type & UDev_Touchscreen)
udev_enumerate_add_match_property(ue, "ID_INPUT_TOUCHSCREEN", "1");
udev_enumerate_scan_devices(ue);
udev_list_entry *entry;
udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(ue)) {
const char *syspath = udev_list_entry_get_name(entry);
udev_device *udevice = udev_device_new_from_syspath(u, syspath);
QString candidate = QString::fromLocal8Bit(udev_device_get_devnode(udevice));
udev_device_unref(udevice);
if (path->isEmpty() && candidate.startsWith(QLatin1String("/dev/input/event")))
*path = candidate;
}
udev_enumerate_unref(ue);
udev_unref(u);
}
QT_END_NAMESPACE

View File

@ -3,7 +3,7 @@
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/ ** Contact: http://www.qt-project.org/
** **
** This file is part of the plugins module of the Qt Toolkit. ** This file is part of the plugins of the Qt Toolkit.
** **
** $QT_BEGIN_LICENSE:LGPL$ ** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage ** GNU Lesser General Public License Usage
@ -39,39 +39,21 @@
** **
****************************************************************************/ ****************************************************************************/
#ifndef QTOUCHEVENTSENDERQPA_H #ifndef QUDEVHELPER_P_H
#define QTOUCHEVENTSENDERQPA_H #define QUDEVHELPER_P_H
#include "qtouchscreen.h" #include <QString>
QT_BEGIN_HEADER
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QTouchDevice; enum QUDeviceType {
UDev_Mouse = 0x01,
class QTouchEventSenderQPA : public QTouchScreenObserver UDev_Touchpad = 0x02,
{ UDev_Touchscreen = 0x04
public:
QTouchEventSenderQPA(const QString &spec = QString());
void touch_configure(int x_min, int x_max, int y_min, int y_max,
int pressure_min, int pressure_max, const QString &dev_name);
void touch_point(const QList<QWindowSystemInterface::TouchPoint> &points);
private:
bool m_forceToActiveWindow;
int hw_range_x_min;
int hw_range_x_max;
int hw_range_y_min;
int hw_range_y_max;
int hw_pressure_min;
int hw_pressure_max;
QString hw_dev_name;
QTouchDevice *m_device;
}; };
void q_udev_devicePath(int type, QString *path);
QT_END_NAMESPACE QT_END_NAMESPACE
QT_END_HEADER #endif // QUDEVHELPER_P_H
#endif // QTOUCHEVENTSENDERQPA_H

View File

@ -0,0 +1,5 @@
contains(QT_CONFIG, libudev) {
HEADERS += $$PWD/qudevhelper_p.h
SOURCES += $$PWD/qudevhelper.cpp
LIBS += -ludev
}

View File

@ -0,0 +1,11 @@
Generic plug-in for absolute & relative evdev pointer events.
To use it, launch apps with -plugin EvdevMouse
The plug-in will try to pick a mouse or touchpad device from udev.
If automatic detection does not work, use -plugin
EvdevMouse:/dev/input/eventN to explicitly set the device node.
The initial cursor position is assumed to be (0, 0). Relative events
will generate Qt mouse events with screen positions relative to this
initial position.

View File

@ -0,0 +1,13 @@
TARGET = qevdevmouseplugin
load(qt_plugin)
DESTDIR = $$QT.gui.plugins/generic
target.path = $$[QT_INSTALL_PLUGINS]/generic
INSTALLS += target
HEADERS = qevdevmouse.h
QT += core-private platformsupport-private
SOURCES = main.cpp \
qevdevmouse.cpp

View File

@ -40,38 +40,38 @@
****************************************************************************/ ****************************************************************************/
#include <qgenericplugin_qpa.h> #include <qgenericplugin_qpa.h>
#include "qlinuxinput.h" #include "qevdevmouse.h"
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QLinuxInputPlugin : public QGenericPlugin class QEvdevMousePlugin : public QGenericPlugin
{ {
public: public:
QLinuxInputPlugin(); QEvdevMousePlugin();
QStringList keys() const; QStringList keys() const;
QObject* create(const QString &key, const QString &specification); QObject* create(const QString &key, const QString &specification);
}; };
QLinuxInputPlugin::QLinuxInputPlugin() QEvdevMousePlugin::QEvdevMousePlugin()
: QGenericPlugin() : QGenericPlugin()
{ {
} }
QStringList QLinuxInputPlugin::keys() const QStringList QEvdevMousePlugin::keys() const
{ {
return (QStringList() return (QStringList()
<< QLatin1String("LinuxInputMouse")); << QLatin1String("EvdevMouse"));
} }
QObject* QLinuxInputPlugin::create(const QString &key, QObject* QEvdevMousePlugin::create(const QString &key,
const QString &specification) const QString &specification)
{ {
if (!key.compare(QLatin1String("LinuxInputMouse"), Qt::CaseInsensitive)) if (!key.compare(QLatin1String("EvdevMouse"), Qt::CaseInsensitive))
return new QLinuxInputMouseHandler(key, specification); return new QEvdevMouseHandler(key, specification);
return 0; return 0;
} }
Q_EXPORT_PLUGIN2(qlinuxinputplugin, QLinuxInputPlugin) Q_EXPORT_PLUGIN2(qevdevmouseplugin, QEvdevMousePlugin)
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -0,0 +1,214 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qevdevmouse.h"
#include <QSocketNotifier>
#include <QStringList>
#include <QPoint>
#include <QWindowSystemInterface>
#include <qplatformdefs.h>
#include <private/qcore_unix_p.h> // overrides QT_OPEN
#include <QtPlatformSupport/private/qudevhelper_p.h>
#include <errno.h>
#include <linux/kd.h>
#include <linux/input.h>
#include <qdebug.h>
QT_BEGIN_NAMESPACE
QEvdevMouseHandler::QEvdevMouseHandler(const QString &key,
const QString &specification)
: m_notify(0), m_x(0), m_y(0), m_prevx(0), m_prevy(0),
m_xoffset(0), m_yoffset(0), m_buttons(0)
{
Q_UNUSED(key);
setObjectName(QLatin1String("Evdev Mouse Handler"));
QString dev;
q_udev_devicePath(UDev_Mouse | UDev_Touchpad, &dev);
if (dev.isEmpty())
dev = QLatin1String("/dev/input/event0");
m_compression = true;
m_smooth = false;
int jitterLimit = 0;
QStringList args = specification.split(QLatin1Char(':'));
foreach (const QString &arg, args) {
if (arg == "nocompress")
m_compression = false;
else if (arg.startsWith("dejitter="))
jitterLimit = arg.mid(9).toInt();
else if (arg.startsWith("xoffset="))
m_xoffset = arg.mid(8).toInt();
else if (arg.startsWith("yoffset="))
m_yoffset = arg.mid(8).toInt();
else if (arg.startsWith(QLatin1String("/dev/")))
dev = arg;
}
m_jitterLimitSquared = jitterLimit*jitterLimit;
qDebug("evdevmouse: Using device %s", qPrintable(dev));
m_fd = QT_OPEN(dev.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0);
if (m_fd >= 0) {
m_notify = new QSocketNotifier(m_fd, QSocketNotifier::Read, this);
connect(m_notify, SIGNAL(activated(int)), this, SLOT(readMouseData()));
} else {
qWarning("Cannot open mouse input device '%s': %s", qPrintable(dev), strerror(errno));
return;
}
}
QEvdevMouseHandler::~QEvdevMouseHandler()
{
if (m_fd >= 0)
QT_CLOSE(m_fd);
}
void QEvdevMouseHandler::sendMouseEvent()
{
QPoint pos(m_x + m_xoffset, m_y + m_yoffset);
//qDebug("mouse event %d %d %d", pos.x(), pos.y(), int(m_buttons));
QWindowSystemInterface::handleMouseEvent(0, pos, pos, m_buttons);
m_prevx = m_x;
m_prevy = m_y;
}
void QEvdevMouseHandler::readMouseData()
{
struct ::input_event buffer[32];
int n = 0;
bool posChanged = false;
bool pendingMouseEvent = false;
int eventCompressCount = 0;
forever {
n = QT_READ(m_fd, reinterpret_cast<char *>(buffer) + n, sizeof(buffer) - n);
if (n == 0) {
qWarning("Got EOF from the input device.");
return;
} else if (n < 0 && (errno != EINTR && errno != EAGAIN)) {
qWarning("Could not read from input device: %s", strerror(errno));
return;
} else if (n % sizeof(buffer[0]) == 0) {
break;
}
}
n /= sizeof(buffer[0]);
for (int i = 0; i < n; ++i) {
struct ::input_event *data = &buffer[i];
//qDebug() << ">>" << hex << data->type << data->code << dec << data->value;
if (data->type == EV_ABS) {
if (data->code == ABS_X && m_x != data->value) {
m_x = data->value;
posChanged = true;
} else if (data->code == ABS_Y && m_y != data->value) {
m_y = data->value;
posChanged = true;
}
} else if (data->type == EV_REL) {
if (data->code == REL_X) {
m_x += data->value;
posChanged = true;
} else if (data->code == REL_Y) {
m_y += data->value;
posChanged = true;
} else if (data->code == ABS_WHEEL) { // vertical scroll
// data->value: 1 == up, -1 == down
int delta = 120 * data->value;
QWindowSystemInterface::handleWheelEvent(0, QPoint(m_x, m_y),
QPoint(m_x, m_y),
delta, Qt::Vertical);
} else if (data->code == ABS_THROTTLE) { // horizontal scroll
// data->value: 1 == right, -1 == left
int delta = 120 * -data->value;
QWindowSystemInterface::handleWheelEvent(0, QPoint(m_x, m_y),
QPoint(m_x, m_y),
delta, Qt::Horizontal);
}
} else if (data->type == EV_KEY && data->code == BTN_TOUCH) {
m_buttons = data->value ? Qt::LeftButton : Qt::NoButton;
sendMouseEvent();
pendingMouseEvent = false;
} else if (data->type == EV_KEY && data->code >= BTN_LEFT && data->code <= BTN_MIDDLE) {
Qt::MouseButton button = Qt::NoButton;
switch (data->code) {
case BTN_LEFT: button = Qt::LeftButton; break;
case BTN_MIDDLE: button = Qt::MidButton; break;
case BTN_RIGHT: button = Qt::RightButton; break;
}
if (data->value)
m_buttons |= button;
else
m_buttons &= ~button;
sendMouseEvent();
pendingMouseEvent = false;
} else if (data->type == EV_SYN && data->code == SYN_REPORT) {
if (posChanged) {
posChanged = false;
if (m_compression) {
pendingMouseEvent = true;
eventCompressCount++;
} else {
sendMouseEvent();
}
}
} else if (data->type == EV_MSC && data->code == MSC_SCAN) {
// kernel encountered an unmapped key - just ignore it
continue;
}
}
if (m_compression && pendingMouseEvent) {
int distanceSquared = (m_x - m_prevx)*(m_x - m_prevx) + (m_y - m_prevy)*(m_y - m_prevy);
if (distanceSquared > m_jitterLimitSquared)
sendMouseEvent();
}
}
QT_END_NAMESPACE

View File

@ -39,12 +39,11 @@
** **
****************************************************************************/ ****************************************************************************/
#ifndef QLINUXINPUT_H #ifndef QEVDEVMOUSE_H
#define QLINUXINPUT_H #define QEVDEVMOUSE_H
#include <qobject.h> #include <QObject>
#include <Qt> #include <QString>
#include <termios.h>
QT_BEGIN_HEADER QT_BEGIN_HEADER
@ -52,35 +51,34 @@ QT_BEGIN_NAMESPACE
class QSocketNotifier; class QSocketNotifier;
class QLinuxInputMouseHandlerData; class QEvdevMouseHandler : public QObject
class QLinuxInputMouseHandler : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
QLinuxInputMouseHandler(const QString &key, const QString &specification); QEvdevMouseHandler(const QString &key, const QString &specification);
~QLinuxInputMouseHandler(); ~QEvdevMouseHandler();
private slots: private slots:
void readMouseData(); void readMouseData();
private: private:
void sendMouseEvent(int x, int y, Qt::MouseButtons buttons); void sendMouseEvent();
QSocketNotifier * m_notify; void pathFromUdev(QString *path);
int m_fd;
int m_x, m_y; QSocketNotifier *m_notify;
int m_fd;
int m_x, m_y;
int m_prevx, m_prevy; int m_prevx, m_prevy;
int m_xoffset, m_yoffset; int m_xoffset, m_yoffset;
int m_smoothx, m_smoothy; int m_smoothx, m_smoothy;
Qt::MouseButtons m_buttons; Qt::MouseButtons m_buttons;
bool m_compression; bool m_compression;
bool m_smooth; bool m_smooth;
int m_jitterLimitSquared; int m_jitterLimitSquared;
QLinuxInputMouseHandlerData *d;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE
QT_END_HEADER QT_END_HEADER
#endif // QLINUXINPUT_H #endif // QEVDEVMOUSE_H

View File

@ -0,0 +1,36 @@
Generic plug-in for evdev touch events. (protocol type A)
Tested with the following drivers: bcm5974, hid_magicmouse.
To use it, pass -plugin EvdevTouch on the command line.
If automatic detection does not work, use -plugin
EvdevTouch:/dev/input/eventN to explicitly set the device file
name.
By default the surface of the touch device is mapped to the entire
screen. If this is not desired, pass force_window in the plugin
specification as shown in the example above. This will cause mapping
the touch surface to the active window instead. For example:
./fingerpaint -plugin EvdevTouch:force_window
Only touch events are generated, mouse events are not. Be aware however
that ignored touch events will generate a mouse event from the first
touch point by default. See AA_SynthesizeMouseForUnhandledTouchEvents.
If no evdev events are read, disable the synaptics driver from X or
temporarily disable the device by running
xinput set-prop <device> <device enabled property> 0.
Use xinput list and xinput list-props to figure out the values.
When not running on a windowing system (eglfs, kms, etc.) and having a
touchpad, the evdevmouse and touch plugins can be combined to get both
mouse and touch events:
./app -platform kms -plugin EvdevTouch -plugin EvdevMouse
If the input device cannot be accessed, set up a udev rule.
For example:
sudo cp 70-qtouchscreen.rules /etc/udev/rules.d
sudo udevadm trigger --subsystem-match=input
The udev rule matches any touchpad or touchscreen device. If there are
multiple ones, specify the device manually as described above.

View File

@ -0,0 +1,14 @@
TARGET = qevdevtouchplugin
load(qt_plugin)
DESTDIR = $$QT.gui.plugins/generic
target.path = $$[QT_INSTALL_PLUGINS]/generic
INSTALLS += target
HEADERS = \
qevdevtouch.h
SOURCES = main.cpp \
qevdevtouch.cpp
QT += core-private platformsupport-private

View File

@ -40,8 +40,7 @@
****************************************************************************/ ****************************************************************************/
#include <qgenericplugin_qpa.h> #include <qgenericplugin_qpa.h>
#include "qtouchscreen.h" #include "qevdevtouch.h"
#include "qtoucheventsenderqpa.h"
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -60,21 +59,18 @@ QTouchScreenPlugin::QTouchScreenPlugin()
QStringList QTouchScreenPlugin::keys() const QStringList QTouchScreenPlugin::keys() const
{ {
return QStringList() << "LinuxTouchScreen"; return QStringList() << "EvdevTouch";
} }
QObject* QTouchScreenPlugin::create(const QString &key, QObject* QTouchScreenPlugin::create(const QString &key,
const QString &spec) const QString &spec)
{ {
if (!key.compare(QLatin1String("LinuxTouchScreen"), Qt::CaseInsensitive)) { if (!key.compare(QLatin1String("EvdevTouch"), Qt::CaseInsensitive))
QTouchScreenObserver *obs = new QTouchEventSenderQPA(spec); return new QTouchScreenHandlerThread(spec);
QTouchScreenHandlerThread *h = new QTouchScreenHandlerThread(spec, obs);
return h;
}
return 0; return 0;
} }
Q_EXPORT_PLUGIN2(qtouchscreenplugin, QTouchScreenPlugin) Q_EXPORT_PLUGIN2(qevdevtouchplugin, QTouchScreenPlugin)
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -39,13 +39,15 @@
** **
****************************************************************************/ ****************************************************************************/
#include "qtouchscreen.h" #include "qevdevtouch.h"
#include <QStringList> #include <QStringList>
#include <QHash> #include <QHash>
#include <QSocketNotifier> #include <QSocketNotifier>
#include <QGuiApplication>
#include <QDebug>
#include <QtCore/private/qcore_unix_p.h> #include <QtCore/private/qcore_unix_p.h>
#include <QtPlatformSupport/private/qudevhelper_p.h>
#include <linux/input.h> #include <linux/input.h>
#include <libudev.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -77,6 +79,8 @@ public:
Contact m_currentData; Contact m_currentData;
int findClosestContact(const QHash<int, Contact> &contacts, int x, int y, int *dist); int findClosestContact(const QHash<int, Contact> &contacts, int x, int y, int *dist);
void reportPoints();
void registerDevice();
int hw_range_x_min; int hw_range_x_min;
int hw_range_x_max; int hw_range_x_max;
@ -85,8 +89,8 @@ public:
int hw_pressure_min; int hw_pressure_min;
int hw_pressure_max; int hw_pressure_max;
QString hw_name; QString hw_name;
bool m_forceToActiveWindow;
QList<QTouchScreenObserver *> m_observers; QTouchDevice *m_device;
}; };
QTouchScreenData::QTouchScreenData(QTouchScreenHandler *q_ptr, const QStringList &args) QTouchScreenData::QTouchScreenData(QTouchScreenHandler *q_ptr, const QStringList &args)
@ -96,23 +100,37 @@ QTouchScreenData::QTouchScreenData(QTouchScreenHandler *q_ptr, const QStringList
hw_range_y_min(0), hw_range_y_max(0), hw_range_y_min(0), hw_range_y_max(0),
hw_pressure_min(0), hw_pressure_max(0) hw_pressure_min(0), hw_pressure_max(0)
{ {
Q_UNUSED(args); m_forceToActiveWindow = args.contains(QLatin1String("force_window"));
}
void QTouchScreenData::registerDevice()
{
m_device = new QTouchDevice;
m_device->setName(hw_name);
m_device->setType(QTouchDevice::TouchScreen);
m_device->setCapabilities(QTouchDevice::Position | QTouchDevice::Area);
if (hw_pressure_max > hw_pressure_min)
m_device->setCapabilities(m_device->capabilities() | QTouchDevice::Pressure);
QWindowSystemInterface::registerTouchDevice(m_device);
} }
QTouchScreenHandler::QTouchScreenHandler(const QString &spec) QTouchScreenHandler::QTouchScreenHandler(const QString &spec)
: m_notify(0), m_fd(-1), d(0) : m_notify(0), m_fd(-1), d(0)
{ {
setObjectName(QLatin1String("Linux Touch Handler")); setObjectName(QLatin1String("Evdev Touch Handler"));
QString dev = QLatin1String("/dev/input/event5"); QString dev;
try_udev(&dev); q_udev_devicePath(UDev_Touchpad | UDev_Touchscreen, &dev);
if (dev.isEmpty())
dev = QLatin1String("/dev/input/event0");
QStringList args = spec.split(QLatin1Char(':')); QStringList args = spec.split(QLatin1Char(':'));
for (int i = 0; i < args.count(); ++i) for (int i = 0; i < args.count(); ++i)
if (args.at(i).startsWith(QLatin1String("/dev/"))) if (args.at(i).startsWith(QLatin1String("/dev/")))
dev = args.at(i); dev = args.at(i);
qDebug("Using device '%s'", qPrintable(dev)); qDebug("evdevtouch: Using device %s", qPrintable(dev));
m_fd = QT_OPEN(dev.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0); m_fd = QT_OPEN(dev.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0);
if (m_fd >= 0) { if (m_fd >= 0) {
@ -149,6 +167,8 @@ QTouchScreenHandler::QTouchScreenHandler(const QString &spec)
d->hw_name = QString::fromLocal8Bit(name); d->hw_name = QString::fromLocal8Bit(name);
qDebug("device name: %s", name); qDebug("device name: %s", name);
} }
d->registerDevice();
} }
QTouchScreenHandler::~QTouchScreenHandler() QTouchScreenHandler::~QTouchScreenHandler()
@ -159,38 +179,6 @@ QTouchScreenHandler::~QTouchScreenHandler()
delete d; delete d;
} }
void QTouchScreenHandler::addObserver(QTouchScreenObserver *observer)
{
if (!d || !observer)
return;
d->m_observers.append(observer);
observer->touch_configure(d->hw_range_x_min, d->hw_range_x_max,
d->hw_range_y_min, d->hw_range_y_max,
d->hw_pressure_min, d->hw_pressure_max,
d->hw_name);
}
void QTouchScreenHandler::try_udev(QString *path)
{
*path = QString();
udev *u = udev_new();
udev_enumerate *ue = udev_enumerate_new(u);
udev_enumerate_add_match_subsystem(ue, "input");
udev_enumerate_add_match_property(ue, "ID_INPUT_TOUCHPAD", "1");
udev_enumerate_scan_devices(ue);
udev_list_entry *entry;
udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(ue)) {
const char *syspath = udev_list_entry_get_name(entry);
udev_device *udevice = udev_device_new_from_syspath(u, syspath);
QString candidate = QString::fromLocal8Bit(udev_device_get_devnode(udevice));
udev_device_unref(udevice);
if (path->isEmpty() && candidate.startsWith("/dev/input/event"))
*path = candidate;
}
udev_enumerate_unref(ue);
udev_unref(u);
}
void QTouchScreenHandler::readData() void QTouchScreenHandler::readData()
{ {
::input_event buffer[32]; ::input_event buffer[32];
@ -289,7 +277,7 @@ void QTouchScreenData::processInputEvent(input_event *data)
tp.state = contact.state; tp.state = contact.state;
combinedStates |= tp.state; combinedStates |= tp.state;
// Store the HW coordinates. Observers can then map it to screen space or something else. // Store the HW coordinates for now, will be updated later.
tp.area = QRectF(0, 0, contact.maj, contact.maj); tp.area = QRectF(0, 0, contact.maj, contact.maj);
tp.area.moveCenter(QPoint(contact.x, contact.y)); tp.area.moveCenter(QPoint(contact.x, contact.y));
tp.pressure = contact.pressure; tp.pressure = contact.pressure;
@ -307,10 +295,8 @@ void QTouchScreenData::processInputEvent(input_event *data)
m_lastContacts = m_contacts; m_lastContacts = m_contacts;
m_contacts.clear(); m_contacts.clear();
if (!m_touchPoints.isEmpty() && combinedStates != Qt::TouchPointStationary) { if (!m_touchPoints.isEmpty() && combinedStates != Qt::TouchPointStationary)
for (int i = 0; i < m_observers.count(); ++i) reportPoints();
m_observers.at(i)->touch_point(m_touchPoints);
}
} }
m_lastEventType = data->type; m_lastEventType = data->type;
@ -369,10 +355,48 @@ void QTouchScreenData::assignIds()
m_contacts = newContacts; m_contacts = newContacts;
} }
void QTouchScreenData::reportPoints()
{
QRect winRect;
if (m_forceToActiveWindow) {
QWindow *win = QGuiApplication::activeWindow();
if (!win)
return;
winRect = win->geometry();
} else {
winRect = QGuiApplication::primaryScreen()->geometry();
}
QTouchScreenHandlerThread::QTouchScreenHandlerThread(const QString &spec, const int hw_w = hw_range_x_max - hw_range_x_min;
QTouchScreenObserver *observer) const int hw_h = hw_range_y_max - hw_range_y_min;
: m_spec(spec), m_handler(0), m_observer(observer)
// Map the coordinates based on the normalized position. QPA expects 'area'
// to be in screen coordinates.
const int pointCount = m_touchPoints.count();
for (int i = 0; i < pointCount; ++i) {
QWindowSystemInterface::TouchPoint &tp(m_touchPoints[i]);
// Generate a screen position that is always inside the active window
// or the primary screen.
const int wx = winRect.left() + int(tp.normalPosition.x() * winRect.width());
const int wy = winRect.top() + int(tp.normalPosition.y() * winRect.height());
const qreal sizeRatio = (winRect.width() + winRect.height()) / qreal(hw_w + hw_h);
tp.area = QRect(0, 0, tp.area.width() * sizeRatio, tp.area.height() * sizeRatio);
tp.area.moveCenter(QPoint(wx, wy));
// Calculate normalized pressure.
if (!hw_pressure_min && !hw_pressure_max)
tp.pressure = tp.state == Qt::TouchPointReleased ? 0 : 1;
else
tp.pressure = (tp.pressure - hw_pressure_min) / qreal(hw_pressure_max - hw_pressure_min);
}
QWindowSystemInterface::handleTouchEvent(0, m_device, m_touchPoints);
}
QTouchScreenHandlerThread::QTouchScreenHandlerThread(const QString &spec)
: m_spec(spec), m_handler(0)
{ {
start(); start();
} }
@ -386,7 +410,6 @@ QTouchScreenHandlerThread::~QTouchScreenHandlerThread()
void QTouchScreenHandlerThread::run() void QTouchScreenHandlerThread::run()
{ {
m_handler = new QTouchScreenHandler(m_spec); m_handler = new QTouchScreenHandler(m_spec);
m_handler->addObserver(m_observer);
exec(); exec();
delete m_handler; delete m_handler;
m_handler = 0; m_handler = 0;

View File

@ -39,8 +39,8 @@
** **
****************************************************************************/ ****************************************************************************/
#ifndef QTOUCHSCREEN_H #ifndef QEVDEVTOUCH_H
#define QTOUCHSCREEN_H #define QEVDEVTOUCH_H
#include <QObject> #include <QObject>
#include <QString> #include <QString>
@ -55,14 +55,6 @@ QT_BEGIN_NAMESPACE
class QSocketNotifier; class QSocketNotifier;
class QTouchScreenData; class QTouchScreenData;
class QTouchScreenObserver
{
public:
virtual void touch_configure(int x_min, int x_max, int y_min, int y_max,
int pressure_min, int pressure_max, const QString &dev_name) = 0;
virtual void touch_point(const QList<QWindowSystemInterface::TouchPoint> &points) = 0;
};
class QTouchScreenHandler : public QObject class QTouchScreenHandler : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -70,13 +62,12 @@ class QTouchScreenHandler : public QObject
public: public:
QTouchScreenHandler(const QString &spec = QString()); QTouchScreenHandler(const QString &spec = QString());
~QTouchScreenHandler(); ~QTouchScreenHandler();
void addObserver(QTouchScreenObserver *observer);
private slots: private slots:
void readData(); void readData();
private: private:
void try_udev(QString *path); void pathFromUdev(QString *path);
QSocketNotifier *m_notify; QSocketNotifier *m_notify;
int m_fd; int m_fd;
@ -86,7 +77,7 @@ private:
class QTouchScreenHandlerThread : public QThread class QTouchScreenHandlerThread : public QThread
{ {
public: public:
QTouchScreenHandlerThread(const QString &spec, QTouchScreenObserver *observer); QTouchScreenHandlerThread(const QString &spec);
~QTouchScreenHandlerThread(); ~QTouchScreenHandlerThread();
void run(); void run();
QTouchScreenHandler *handler() { return m_handler; } QTouchScreenHandler *handler() { return m_handler; }
@ -94,11 +85,10 @@ public:
private: private:
QString m_spec; QString m_spec;
QTouchScreenHandler *m_handler; QTouchScreenHandler *m_handler;
QTouchScreenObserver *m_observer;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE
QT_END_HEADER QT_END_HEADER
#endif // QTOUCHSCREEN_H #endif // QEVDEVTOUCH_H

View File

@ -1,3 +1,9 @@
TEMPLATE = subdirs TEMPLATE = subdirs
linux-g++-maemo: SUBDIRS += meego linux-g++-maemo: SUBDIRS += meego
contains(QT_CONFIG, evdev) {
contains(QT_CONFIG, libudev) {
SUBDIRS += evdevmouse evdevtouch
}
}

View File

@ -1,14 +0,0 @@
TARGET = qlinuxinputplugin
load(qt_plugin)
DESTDIR = $$QT.gui.plugins/generic
target.path = $$[QT_INSTALL_PLUGINS]/generic
INSTALLS += target
HEADERS = qlinuxinput.h
QT += core-private
SOURCES = main.cpp \
qlinuxinput.cpp

View File

@ -1,365 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qlinuxinput.h"
#include <QSocketNotifier>
#include <QStringList>
#include <QPoint>
#include <QWindowSystemInterface>
#include <qplatformdefs.h>
#include <private/qcore_unix_p.h> // overrides QT_OPEN
#include <errno.h>
#include <termios.h>
#include <linux/kd.h>
#include <linux/input.h>
#include <qdebug.h>
QT_BEGIN_NAMESPACE
//#define QT_QPA_EXPERIMENTAL_TOUCHEVENT
#ifdef QT_QPA_EXPERIMENTAL_TOUCHEVENT
class QLinuxInputMouseHandlerData
{
public:
QLinuxInputMouseHandlerData() :seenMT(false), state(QEvent::TouchBegin), currentIdx(0) {}
void ensureCurrentPoint() {
if (currentIdx >= touchPoints.size()) {
Q_ASSERT(currentIdx == touchPoints.size());
QWindowSystemInterface::TouchPoint tp;
tp.id = currentIdx;
tp.isPrimary = (currentIdx == 0);
tp.pressure = 1;
tp.area = QRectF(0,0,1,1);
tp.state = Qt::TouchPointReleased; // init in neutral state
touchPoints.append(tp);
}
}
void setCurrentPoint(int i) {
currentIdx = i;
if (currentIdx < touchPoints.size()) {
currentX = int(touchPoints[currentIdx].area.left());
currentY = int(touchPoints[currentIdx].area.top());
} else {
currentY = currentX = -999;
}
}
void advanceCurrentPoint() {
setCurrentPoint(currentIdx + 1);
}
int currentPoint() { return currentIdx; }
void setCurrentX(int value) {
ensureCurrentPoint();
touchPoints[currentIdx].area.moveLeft(value);
}
bool currentMoved() {
return currentX != touchPoints[currentIdx].area.left() || currentY != touchPoints[currentIdx].area.top();
}
void updateCurrentPos() {
ensureCurrentPoint();
touchPoints[currentIdx].area.moveTopLeft(QPointF(currentX, currentY));
}
void setCurrentState(Qt::TouchPointState state) {
ensureCurrentPoint();
touchPoints[currentIdx].state = state;
}
Qt::TouchPointState currentState() const {
if (currentIdx < touchPoints.size())
return touchPoints[currentIdx].state;
return Qt::TouchPointReleased;
}
QList<QWindowSystemInterface::TouchPoint> touchPoints;
int currentX;
int currentY;
bool seenMT;
QEvent::Type state;
private:
int currentIdx;
};
#endif
QLinuxInputMouseHandler::QLinuxInputMouseHandler(const QString &key,
const QString &specification)
: m_notify(0), m_x(0), m_y(0), m_prevx(0), m_prevy(0), m_xoffset(0), m_yoffset(0), m_buttons(0), d(0)
{
qDebug() << "QLinuxInputMouseHandler" << key << specification;
setObjectName(QLatin1String("LinuxInputSubsystem Mouse Handler"));
QString dev = QLatin1String("/dev/input/event0");
m_compression = true;
m_smooth = false;
int jitterLimit = 0;
QStringList args = specification.split(QLatin1Char(':'));
foreach (const QString &arg, args) {
if (arg == "nocompress")
m_compression = false;
else if (arg.startsWith("dejitter="))
jitterLimit = arg.mid(9).toInt();
else if (arg.startsWith("xoffset="))
m_xoffset = arg.mid(8).toInt();
else if (arg.startsWith("yoffset="))
m_yoffset = arg.mid(8).toInt();
else if (arg.startsWith(QLatin1String("/dev/")))
dev = arg;
}
m_jitterLimitSquared = jitterLimit*jitterLimit;
m_fd = QT_OPEN(dev.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0);
if (m_fd >= 0) {
m_notify = new QSocketNotifier(m_fd, QSocketNotifier::Read, this);
connect(m_notify, SIGNAL(activated(int)), this, SLOT(readMouseData()));
} else {
qWarning("Cannot open mouse input device '%s': %s", qPrintable(dev), strerror(errno));
return;
}
#ifdef QT_QPA_EXPERIMENTAL_TOUCHEVENT
d = new QLinuxInputMouseHandlerData;
#endif
}
QLinuxInputMouseHandler::~QLinuxInputMouseHandler()
{
if (m_fd >= 0)
QT_CLOSE(m_fd);
#ifdef QT_QPA_EXPERIMENTAL_TOUCHEVENT
delete d;
#endif
}
void QLinuxInputMouseHandler::sendMouseEvent(int x, int y, Qt::MouseButtons buttons)
{
QPoint pos(x+m_xoffset, y+m_yoffset);
QWindowSystemInterface::handleMouseEvent(0, pos, pos, m_buttons);
m_prevx = x;
m_prevy = y;
}
void QLinuxInputMouseHandler::readMouseData()
{
struct ::input_event buffer[32];
int n = 0;
bool posChanged = false;
bool pendingMouseEvent = false;
int eventCompressCount = 0;
forever {
n = QT_READ(m_fd, reinterpret_cast<char *>(buffer) + n, sizeof(buffer) - n);
if (n == 0) {
qWarning("Got EOF from the input device.");
return;
} else if (n < 0 && (errno != EINTR && errno != EAGAIN)) {
qWarning("Could not read from input device: %s", strerror(errno));
return;
} else if (n % sizeof(buffer[0]) == 0) {
break;
}
}
n /= sizeof(buffer[0]);
for (int i = 0; i < n; ++i) {
struct ::input_event *data = &buffer[i];
//qDebug() << ">>" << hex << data->type << data->code << dec << data->value;
bool unknown = false;
if (data->type == EV_ABS) {
if (data->code == ABS_X && m_x != data->value) {
m_x = data->value;
posChanged = true;
} else if (data->code == ABS_Y && m_y != data->value) {
m_y = data->value;
posChanged = true;
} else if (data->code == ABS_PRESSURE) {
//ignore for now...
} else if (data->code == ABS_TOOL_WIDTH) {
//ignore for now...
} else if (data->code == ABS_HAT0X) {
//ignore for now...
} else if (data->code == ABS_HAT0Y) {
//ignore for now...
#ifdef QT_QPA_EXPERIMENTAL_TOUCHEVENT
} else if (data->code == ABS_MT_POSITION_X) {
d->currentX = data->value;
d->seenMT = true;
} else if (data->code == ABS_MT_POSITION_Y) {
d->currentY = data->value;
d->seenMT = true;
} else if (data->code == ABS_MT_TOUCH_MAJOR) {
if (data->value == 0)
d->setCurrentState(Qt::TouchPointReleased);
//otherwise, ignore for now...
} else if (data->code == ABS_MT_TOUCH_MINOR) {
//ignore for now...
#endif
} else {
unknown = true;
}
} else if (data->type == EV_REL) {
if (data->code == REL_X) {
m_x += data->value;
posChanged = true;
} else if (data->code == REL_Y) {
m_y += data->value;
posChanged = true;
} else if (data->code == ABS_WHEEL) { // vertical scroll
// data->value: 1 == up, -1 == down
int delta = 120 * data->value;
QWindowSystemInterface::handleWheelEvent(0, QPoint(m_x, m_y),
QPoint(m_x, m_y),
delta, Qt::Vertical);
} else if (data->code == ABS_THROTTLE) { // horizontal scroll
// data->value: 1 == right, -1 == left
int delta = 120 * -data->value;
QWindowSystemInterface::handleWheelEvent(0, QPoint(m_x, m_y),
QPoint(m_x, m_y),
delta, Qt::Horizontal);
} else {
unknown = true;
}
} else if (data->type == EV_KEY && data->code == BTN_TOUCH) {
m_buttons = data->value ? Qt::LeftButton : Qt::NoButton;
sendMouseEvent(m_x, m_y, m_buttons);
pendingMouseEvent = false;
} else if (data->type == EV_KEY && data->code >= BTN_LEFT && data->code <= BTN_MIDDLE) {
Qt::MouseButton button = Qt::NoButton;
switch (data->code) {
case BTN_LEFT: button = Qt::LeftButton; break;
case BTN_MIDDLE: button = Qt::MidButton; break;
case BTN_RIGHT: button = Qt::RightButton; break;
}
if (data->value)
m_buttons |= button;
else
m_buttons &= ~button;
sendMouseEvent(m_x, m_y, m_buttons);
pendingMouseEvent = false;
} else if (data->type == EV_SYN && data->code == SYN_REPORT) {
if (posChanged) {
posChanged = false;
if (m_compression) {
pendingMouseEvent = true;
eventCompressCount++;
} else {
sendMouseEvent(m_x, m_y, m_buttons);
}
}
#ifdef QT_QPA_EXPERIMENTAL_TOUCHEVENT
if (d->state == QEvent::TouchBegin && !d->seenMT) {
//no multipoint-touch events to send
} else {
if (!d->seenMT)
d->state = QEvent::TouchEnd;
for (int i = d->currentPoint(); i < d->touchPoints.size(); ++i) {
d->touchPoints[i].pressure = 0;
d->touchPoints[i].state = Qt::TouchPointReleased;
}
//qDebug() << "handleTouchEvent" << d->state << d->touchPoints.size() << d->touchPoints[0].state;
QWindowSystemInterface::handleTouchEvent(0, d->state, QTouchEvent::TouchScreen, d->touchPoints);
if (d->seenMT) {
d->state = QEvent::TouchUpdate;
} else {
d->state = QEvent::TouchBegin;
d->touchPoints.clear();
}
d->setCurrentPoint(0);
d->seenMT = false;
}
} else if (data->type == EV_SYN && data->code == SYN_MT_REPORT) {
//store data for this touch point
if (!d->seenMT) {
d->setCurrentState(Qt::TouchPointReleased);
} else if (d->currentState() == Qt::TouchPointReleased) {
d->updateCurrentPos();
d->setCurrentState(Qt::TouchPointPressed);
} else if (d->currentMoved()) {
d->updateCurrentPos();
d->setCurrentState(Qt::TouchPointMoved);
} else {
d->setCurrentState(Qt::TouchPointStationary);
}
//qDebug() << "end of point" << d->currentPoint() << d->currentX << d->currentY << d->currentState();
//advance to next tp:
d->advanceCurrentPoint();
#endif
} else if (data->type == EV_MSC && data->code == MSC_SCAN) {
// kernel encountered an unmapped key - just ignore it
continue;
} else {
unknown = true;
}
#ifdef QLINUXINPUT_EXTRA_DEBUG
if (unknown) {
qWarning("unknown mouse event type=%x, code=%x, value=%x", data->type, data->code, data->value);
}
#endif
}
if (m_compression && pendingMouseEvent) {
int distanceSquared = (m_x - m_prevx)*(m_x - m_prevx) + (m_y - m_prevy)*(m_y - m_prevy);
if (distanceSquared > m_jitterLimitSquared)
sendMouseEvent(m_x, m_y, m_buttons);
}
}
QT_END_NAMESPACE

View File

@ -1,45 +0,0 @@
Generic plug-in for evdev touch events.
Tested with the following drivers: bcm5974, hid_magicmouse.
(1) Using as a QPA generic plug-in
1. set up and connect the touch device
2. install libudev-dev or similar
3. build this plug-in (qmake && make)
4. sudo cp 70-qtouchscreen.rules /etc/udev/rules.d
5. sudo udevadm trigger --subsystem-match=input
6. ./fingerpaint -plugin LinuxTouchScreen:force_window
If automatic detection does not work, use -plugin
LinuxTouchScreen:/dev/input/eventN to explicitly set the device file
name.
By default the surface of the touch device is mapped to the entire
screen. If this is not desired, pass force_window in the plugin
specification as shown in the example above. This will cause mapping
the touch surface to the active window instead.
Only touch events are generated, mouse events are not. Be aware however
that ignored touch events will generate a mouse event from the first
touch point by default. See AA_SynthesizeMouseForUnhandledTouchEvents.
(2) Using in a compositor
The classes (QTouchScreenHandler, QTouchScreenHandlerThread) are also
suitable for direct inclusion into an application, e.g. a Wayland
compositor. The compositor may then register its own
QTouchScreenObserver because relying on the QTouchEvents generated by
the QPA event sender may not always be satisfactory as some low-level
details get lost, and due to performance reasons.
(3) Possible issues and solutions
The udev rule matches any touchpad device. If there are multiple ones,
specify the device as described above.
If no evdev events are read, remove 50-synaptics.conf (or similar)
from /usr/share/X11/xorg.conf.d and restart X. Or at least temporarily
disable the device by running xinput set-prop <device> <device enabled
property> 0. Use xinput list and xinput list-props to figure out the
values.

View File

@ -1,116 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the plugins module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qtoucheventsenderqpa.h"
#include <QGuiApplication>
#include <QScreen>
#include <QStringList>
#include <QDebug>
QT_BEGIN_NAMESPACE
QTouchEventSenderQPA::QTouchEventSenderQPA(const QString &spec)
{
m_forceToActiveWindow = spec.split(QLatin1Char(':')).contains(QLatin1String("force_window"));
m_device = new QTouchDevice;
m_device->setType(QTouchDevice::TouchScreen);
m_device->setCapabilities(QTouchDevice::Position | QTouchDevice::Area);
QWindowSystemInterface::registerTouchDevice(m_device);
}
void QTouchEventSenderQPA::touch_configure(int x_min, int x_max, int y_min, int y_max,
int pressure_min, int pressure_max,
const QString &dev_name)
{
hw_range_x_min = x_min;
hw_range_x_max = x_max;
hw_range_y_min = y_min;
hw_range_y_max = y_max;
hw_pressure_min = pressure_min;
hw_pressure_max = pressure_max;
m_device->setName(dev_name);
if (hw_pressure_max > hw_pressure_min)
m_device->setCapabilities(m_device->capabilities() | QTouchDevice::Pressure);
}
void QTouchEventSenderQPA::touch_point(const QList<QWindowSystemInterface::TouchPoint> &points)
{
QRect winRect;
if (m_forceToActiveWindow) {
QWindow *win = QGuiApplication::activeWindow();
if (!win)
return;
winRect = win->geometry();
} else {
winRect = QGuiApplication::primaryScreen()->geometry();
}
const int hw_w = hw_range_x_max - hw_range_x_min;
const int hw_h = hw_range_y_max - hw_range_y_min;
QList<QWindowSystemInterface::TouchPoint> touchPoints = points;
// Map the coordinates based on the normalized position. QPA expects 'area'
// to be in screen coordinates.
for (int i = 0; i < touchPoints.size(); ++i) {
QWindowSystemInterface::TouchPoint &tp(touchPoints[i]);
// Generate a screen position that is always inside the active window
// or the primary screen.
const int wx = winRect.left() + int(tp.normalPosition.x() * winRect.width());
const int wy = winRect.top() + int(tp.normalPosition.y() * winRect.height());
const qreal sizeRatio = (winRect.width() + winRect.height()) / qreal(hw_w + hw_h);
tp.area = QRect(0, 0, tp.area.width() * sizeRatio, tp.area.height() * sizeRatio);
tp.area.moveCenter(QPoint(wx, wy));
// Calculate normalized pressure.
if (!hw_pressure_min && !hw_pressure_max)
tp.pressure = tp.state == Qt::TouchPointReleased ? 0 : 1;
else
tp.pressure = (tp.pressure - hw_pressure_min) / qreal(hw_pressure_max - hw_pressure_min);
}
QWindowSystemInterface::handleTouchEvent(0, m_device, touchPoints);
}
QT_END_NAMESPACE

View File

@ -1,18 +0,0 @@
TARGET = qtouchscreenplugin
load(qt_plugin)
DESTDIR = $$QT.gui.plugins/generic
target.path = $$[QT_INSTALL_PLUGINS]/generic
INSTALLS += target
HEADERS = \
qtouchscreen.h \
qtoucheventsenderqpa.h
SOURCES = main.cpp \
qtouchscreen.cpp \
qtoucheventsenderqpa.cpp
QT += core-private gui-private
LIBS += -ludev