f12cf0efba
With Qt5 QCursor does no longer provide ::handle() which was used to set a cursor on a native XWindow for which we do not have a QWidget. Also KWin has had for quite some time an optimized version to get the cursor position without doing XQueryPointer each time ::pos() is called. These two features are merged into a new class Cursor providing more or less the same API as QCursor. In addition the new class provides a facility to perform mouse polling replacing the implementations in Compositor and ScreenEdges. For more information about the new class see the documentation for the new class in cursor.h.
278 lines
6.9 KiB
C++
278 lines
6.9 KiB
C++
/********************************************************************
|
|
KWin - the KDE window manager
|
|
This file is part of the KDE project.
|
|
|
|
Copyright (C) 2013 Martin Gräßlin <mgraesslin@kde.org>
|
|
|
|
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 "cursor.h"
|
|
// kwin
|
|
#include <kwinglobals.h>
|
|
#include "utils.h"
|
|
// Qt
|
|
#include <QTimer>
|
|
// Xlib
|
|
#include <X11/Xcursor/Xcursor.h>
|
|
#include <fixx11h.h>
|
|
|
|
namespace KWin
|
|
{
|
|
|
|
Cursor *Cursor::s_self = NULL;
|
|
|
|
Cursor::Cursor(QObject *parent)
|
|
: QObject(parent)
|
|
, m_mousePollingCounter(0)
|
|
{
|
|
}
|
|
|
|
Cursor::~Cursor()
|
|
{
|
|
s_self = NULL;
|
|
}
|
|
|
|
Cursor *Cursor::create(QObject *parent)
|
|
{
|
|
Q_ASSERT(!s_self);
|
|
s_self = new X11Cursor(parent);
|
|
return s_self;
|
|
}
|
|
|
|
QPoint Cursor::pos()
|
|
{
|
|
s_self->doGetPos();
|
|
return s_self->m_pos;
|
|
}
|
|
|
|
void Cursor::setPos(const QPoint &pos)
|
|
{
|
|
// first query the current pos to not warp to the already existing pos
|
|
if (pos == Cursor::pos()) {
|
|
return;
|
|
}
|
|
s_self->m_pos = pos;
|
|
s_self->doSetPos();
|
|
}
|
|
|
|
void Cursor::setPos(int x, int y)
|
|
{
|
|
Cursor::setPos(QPoint(x, y));
|
|
}
|
|
|
|
xcb_cursor_t Cursor::getX11Cursor(Qt::CursorShape shape)
|
|
{
|
|
Q_UNUSED(shape)
|
|
return XCB_CURSOR_NONE;
|
|
}
|
|
|
|
xcb_cursor_t Cursor::x11Cursor(Qt::CursorShape shape)
|
|
{
|
|
return s_self->getX11Cursor(shape);
|
|
}
|
|
|
|
void Cursor::doSetPos()
|
|
{
|
|
emit posChanged(m_pos);
|
|
}
|
|
|
|
void Cursor::doGetPos()
|
|
{
|
|
}
|
|
|
|
void Cursor::updatePos(const QPoint &pos)
|
|
{
|
|
if (m_pos == pos) {
|
|
return;
|
|
}
|
|
m_pos = pos;
|
|
emit posChanged(m_pos);
|
|
}
|
|
|
|
void Cursor::startMousePolling()
|
|
{
|
|
++m_mousePollingCounter;
|
|
if (m_mousePollingCounter == 1) {
|
|
doStartMousePolling();
|
|
}
|
|
}
|
|
|
|
void Cursor::stopMousePolling()
|
|
{
|
|
Q_ASSERT(m_mousePollingCounter > 0);
|
|
--m_mousePollingCounter;
|
|
if (m_mousePollingCounter == 0) {
|
|
doStopMousePolling();
|
|
}
|
|
}
|
|
|
|
void Cursor::doStartMousePolling()
|
|
{
|
|
}
|
|
|
|
void Cursor::doStopMousePolling()
|
|
{
|
|
}
|
|
|
|
X11Cursor::X11Cursor(QObject *parent)
|
|
: Cursor(parent)
|
|
, m_timeStamp(XCB_TIME_CURRENT_TIME)
|
|
, m_buttonMask(0)
|
|
, m_resetTimeStampTimer(new QTimer(this))
|
|
, m_mousePollingTimer(new QTimer(this))
|
|
{
|
|
m_resetTimeStampTimer->setSingleShot(true);
|
|
connect(m_resetTimeStampTimer, SIGNAL(timeout()), SLOT(resetTimeStamp()));
|
|
// TODO: How often do we really need to poll?
|
|
m_mousePollingTimer->setInterval(100);
|
|
connect(m_mousePollingTimer, SIGNAL(timeout()), SLOT(mousePolled()));
|
|
}
|
|
|
|
X11Cursor::~X11Cursor()
|
|
{
|
|
}
|
|
|
|
void X11Cursor::doSetPos()
|
|
{
|
|
const QPoint &pos = currentPos();
|
|
xcb_warp_pointer(connection(), XCB_WINDOW_NONE, rootWindow(), 0, 0, 0, 0, pos.x(), pos.y());
|
|
// call default implementation to emit signal
|
|
Cursor::doSetPos();
|
|
}
|
|
|
|
void X11Cursor::doGetPos()
|
|
{
|
|
if (m_timeStamp != XCB_TIME_CURRENT_TIME &&
|
|
m_timeStamp == QX11Info::appTime()) {
|
|
// time stamps did not change, no need to query again
|
|
return;
|
|
}
|
|
m_timeStamp = QX11Info::appTime();
|
|
ScopedCPointer<xcb_query_pointer_reply_t> pointer(xcb_query_pointer_reply(connection(),
|
|
xcb_query_pointer_unchecked(connection(), rootWindow()), NULL));
|
|
if (!pointer) {
|
|
return;
|
|
}
|
|
m_buttonMask = pointer->mask;
|
|
updatePos(pointer->root_x, pointer->root_y);
|
|
m_resetTimeStampTimer->start(0);
|
|
}
|
|
|
|
void X11Cursor::resetTimeStamp()
|
|
{
|
|
m_timeStamp = XCB_TIME_CURRENT_TIME;
|
|
}
|
|
|
|
void X11Cursor::doStartMousePolling()
|
|
{
|
|
m_mousePollingTimer->start();
|
|
}
|
|
|
|
void X11Cursor::doStopMousePolling()
|
|
{
|
|
m_mousePollingTimer->stop();
|
|
}
|
|
|
|
void X11Cursor::mousePolled()
|
|
{
|
|
const QPoint last = currentPos();
|
|
const uint16_t lastMask = m_buttonMask;
|
|
doGetPos(); // Update if needed
|
|
if (last != currentPos() || lastMask != m_buttonMask) {
|
|
emit mouseChanged(currentPos(), last,
|
|
x11ToQtMouseButtons(m_buttonMask), x11ToQtMouseButtons(lastMask),
|
|
x11ToQtKeyboardModifiers(m_buttonMask), x11ToQtKeyboardModifiers(lastMask));
|
|
}
|
|
}
|
|
|
|
xcb_cursor_t X11Cursor::getX11Cursor(Qt::CursorShape shape)
|
|
{
|
|
QHash<Qt::CursorShape, xcb_cursor_t>::const_iterator it = m_cursors.constFind(shape);
|
|
if (it != m_cursors.constEnd()) {
|
|
return it.value();
|
|
}
|
|
return createCursor(shape);
|
|
}
|
|
|
|
xcb_cursor_t X11Cursor::createCursor(Qt::CursorShape shape)
|
|
{
|
|
const QByteArray name = cursorName(shape);
|
|
if (name.isEmpty()) {
|
|
return XCB_CURSOR_NONE;
|
|
}
|
|
// XCursor is an XLib only lib
|
|
const char *theme = XcursorGetTheme(display());
|
|
const int size = XcursorGetDefaultSize(display());
|
|
XcursorImage *ximg = XcursorLibraryLoadImage(name.constData(), theme, size);
|
|
if (!ximg) {
|
|
return XCB_CURSOR_NONE;
|
|
}
|
|
xcb_cursor_t cursor = XcursorImageLoadCursor(display(), ximg);
|
|
XcursorImageDestroy(ximg);
|
|
m_cursors.insert(shape, cursor);
|
|
return cursor;
|
|
}
|
|
|
|
QByteArray X11Cursor::cursorName(Qt::CursorShape shape) const
|
|
{
|
|
switch (shape) {
|
|
case Qt::ArrowCursor:
|
|
return QByteArray("left_ptr");
|
|
case Qt::UpArrowCursor:
|
|
return QByteArray("up_arrow");
|
|
case Qt::CrossCursor:
|
|
return QByteArray("cross");
|
|
case Qt::WaitCursor:
|
|
return QByteArray("wait");
|
|
case Qt::IBeamCursor:
|
|
return QByteArray("ibeam");
|
|
case Qt::SizeVerCursor:
|
|
return QByteArray("size_ver");
|
|
case Qt::SizeHorCursor:
|
|
return QByteArray("size_hor");
|
|
case Qt::SizeBDiagCursor:
|
|
return QByteArray("size_bdiag");
|
|
case Qt::SizeFDiagCursor:
|
|
return QByteArray("size_fdiag");
|
|
case Qt::SizeAllCursor:
|
|
return QByteArray("size_all");
|
|
case Qt::SplitVCursor:
|
|
return QByteArray("split_v");
|
|
case Qt::SplitHCursor:
|
|
return QByteArray("split_h");
|
|
case Qt::PointingHandCursor:
|
|
return QByteArray("pointing_hand");
|
|
case Qt::ForbiddenCursor:
|
|
return QByteArray("forbidden");
|
|
case Qt::OpenHandCursor:
|
|
return QByteArray("openhand");
|
|
case Qt::ClosedHandCursor:
|
|
return QByteArray("closedhand");
|
|
case Qt::WhatsThisCursor:
|
|
return QByteArray("whats_this");
|
|
case Qt::BusyCursor:
|
|
return QByteArray("left_ptr_watch");
|
|
case Qt::DragMoveCursor:
|
|
return QByteArray("dnd-move");
|
|
case Qt::DragCopyCursor:
|
|
return QByteArray("dnd-copy");
|
|
case Qt::DragLinkCursor:
|
|
return QByteArray("dnd-link");
|
|
default:
|
|
return QByteArray();
|
|
}
|
|
}
|
|
|
|
} // namespace
|