summaryrefslogtreecommitdiffstats
path: root/src/plugins/generic/bsdmouse/qbsdmouse.cpp
blob: d4ed6bba2df1b0e7a56028093e86117259f6ea90 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright (C) 2015-2016 Oleksandr Tymoshenko <gonzo@bluezbox.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#include "qbsdmouse.h"

#include <QFile>
#include <QSocketNotifier>
#include <QStringList>
#include <QPoint>
#include <QGuiApplication>
#include <qpa/qwindowsysteminterface.h>
#include <private/qguiapplication_p.h>
#include <private/qinputdevicemanager_p_p.h>

#include <private/qcore_unix_p.h>
#include <qdebug.h>

#include <errno.h>
#include <fcntl.h>
#include <sys/mouse.h>
#include <unistd.h>

QT_BEGIN_NAMESPACE

enum {
    PsmLevelBasic = 0,
    PsmLevelExtended = 1,
    PsmLevelNative = 2
};

QBsdMouseHandler::QBsdMouseHandler(const QString &key, const QString &specification)
{
    Q_UNUSED(key);

    setObjectName(QLatin1String("BSD Sysmouse Handler"));

    QByteArray device;
    if (specification.startsWith("/dev/"))
        device = QFile::encodeName(specification);

    if (device.isEmpty())
        device = QByteArrayLiteral("/dev/sysmouse");

    m_devFd = QT_OPEN(device.constData(), O_RDONLY);
    if (m_devFd < 0) {
        qErrnoWarning(errno, "open(%s) failed", device.constData());
        return;
    }

    int level = 0;
    if (ioctl(m_devFd, MOUSE_GETLEVEL, &level)) {
        qErrnoWarning(errno, "ioctl(%s, MOUSE_GETLEVEL) failed", device.constData());
        close(m_devFd);
        m_devFd = -1;
        return;
    }

    switch (level) {
    case PsmLevelBasic:
        m_packetSize = 5;
        break;
    case PsmLevelExtended:
        m_packetSize = 8;
        break;
    default:
        qWarning("Unsupported mouse device operation level: %d", level);
        close(m_devFd);
        m_devFd = -1;
        return;
    }

    if (fcntl(m_devFd, F_SETFL, O_NONBLOCK)) {
        qErrnoWarning(errno, "fcntl(%s, F_SETFL, O_NONBLOCK) failed", device.constData());
        close(m_devFd);
        m_devFd = -1;
        return;
    }

    m_notifier.reset(new QSocketNotifier(m_devFd, QSocketNotifier::Read, this));
    connect(m_notifier.data(), &QSocketNotifier::activated, this, &QBsdMouseHandler::readMouseData);
    QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount(
        QInputDeviceManager::DeviceTypePointer, 1);
}

QBsdMouseHandler::~QBsdMouseHandler()
{
    if (m_devFd != -1)
        close(m_devFd);
}

void QBsdMouseHandler::readMouseData()
{
    if (m_devFd < 0)
        return;

    if (m_packetSize == 0)
        return;

    // packet format described in mouse(4)
    qint8 packet[MOUSE_SYS_PACKETSIZE];
    quint8 status = 0;
    while (read(m_devFd, packet, m_packetSize) == m_packetSize) {
        const qint16 relx = packet[1] + packet[3];
        const qint16 rely = -(packet[2] + packet[4]);

        m_x += relx;
        m_y += rely;

        status = packet[0] & MOUSE_SYS_STDBUTTONS;
    }

    // clamp to screen geometry
    const QRect g = QGuiApplication::primaryScreen()->virtualGeometry();
    if (m_x + m_xOffset < g.left())
        m_x = g.left() - m_xOffset;
    else if (m_x + m_xOffset > g.right())
        m_x = g.right() - m_xOffset;

    if (m_y + m_yOffset < g.top())
        m_y = g.top() - m_yOffset;
    else if (m_y + m_yOffset > g.bottom())
        m_y = g.bottom() - m_yOffset;

    const QPoint pos(m_x + m_xOffset, m_y + m_yOffset);
    m_buttons = Qt::NoButton;
    if (!(status & MOUSE_SYS_BUTTON1UP))
        m_buttons |= Qt::LeftButton;
    if (!(status & MOUSE_SYS_BUTTON2UP))
        m_buttons |= Qt::MiddleButton;
    if (!(status & MOUSE_SYS_BUTTON3UP))
        m_buttons |= Qt::RightButton;

    QWindowSystemInterface::handleMouseEvent(0, pos, pos, m_buttons);
}

QT_END_NAMESPACE