Add kwin full source tree, greeter login, zsh, pcid service, and build system improvements
This commit is contained in:
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2019 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include "tablet_input.h"
|
||||
#include "core/inputdevice.h"
|
||||
#include "cursorsource.h"
|
||||
#include "decorations/decoratedwindow.h"
|
||||
#include "input_event.h"
|
||||
#include "input_event_spy.h"
|
||||
#include "osd.h"
|
||||
#include "pointer_input.h"
|
||||
#include "wayland/tablet_v2.h"
|
||||
#include "wayland_server.h"
|
||||
#include "window.h"
|
||||
#include "workspace.h"
|
||||
|
||||
#include <KDecoration3/Decoration>
|
||||
#include <KGlobalAccel>
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <QAction>
|
||||
#include <QHoverEvent>
|
||||
#include <QWindow>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class SurfaceCursor : public Cursor
|
||||
{
|
||||
public:
|
||||
explicit SurfaceCursor(TabletToolV2Interface *tool)
|
||||
: Cursor()
|
||||
{
|
||||
setParent(tool);
|
||||
connect(tool, &TabletToolV2Interface::cursorChanged, this, [this](const TabletCursorSourceV2 &cursor) {
|
||||
if (auto surfaceCursor = std::get_if<TabletSurfaceCursorV2 *>(&cursor)) {
|
||||
// If the cursor is unset, fallback to the cross cursor.
|
||||
if ((*surfaceCursor) && (*surfaceCursor)->enteredSerial()) {
|
||||
if (!m_surfaceSource) {
|
||||
m_surfaceSource = std::make_unique<SurfaceCursorSource>();
|
||||
}
|
||||
m_surfaceSource->update((*surfaceCursor)->surface(), (*surfaceCursor)->hotspot());
|
||||
setSource(m_surfaceSource.get());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray shape;
|
||||
if (auto shapeCursor = std::get_if<QByteArray>(&cursor)) {
|
||||
shape = *shapeCursor;
|
||||
} else {
|
||||
shape = QByteArrayLiteral("cross");
|
||||
}
|
||||
|
||||
static WaylandCursorImage defaultCursor;
|
||||
if (!m_shapeSource) {
|
||||
m_shapeSource = std::make_unique<ShapeCursorSource>();
|
||||
}
|
||||
m_shapeSource->setTheme(defaultCursor.theme());
|
||||
m_shapeSource->setShape(shape);
|
||||
setSource(m_shapeSource.get());
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<ShapeCursorSource> m_shapeSource;
|
||||
std::unique_ptr<SurfaceCursorSource> m_surfaceSource;
|
||||
};
|
||||
|
||||
TabletInputRedirection::TabletInputRedirection(InputRedirection *parent)
|
||||
: InputDeviceHandler(parent)
|
||||
{
|
||||
}
|
||||
|
||||
TabletInputRedirection::~TabletInputRedirection() = default;
|
||||
|
||||
void TabletInputRedirection::init()
|
||||
{
|
||||
Q_ASSERT(!inited());
|
||||
setInited(true);
|
||||
InputDeviceHandler::init();
|
||||
|
||||
connect(workspace(), &QObject::destroyed, this, [this] {
|
||||
setInited(false);
|
||||
});
|
||||
connect(waylandServer(), &QObject::destroyed, this, [this] {
|
||||
setInited(false);
|
||||
});
|
||||
|
||||
const auto devices = input()->devices();
|
||||
for (InputDevice *device : devices) {
|
||||
integrateDevice(device);
|
||||
}
|
||||
connect(input(), &InputRedirection::deviceAdded, this, &TabletInputRedirection::integrateDevice);
|
||||
connect(input(), &InputRedirection::deviceRemoved, this, &TabletInputRedirection::removeDevice);
|
||||
|
||||
auto tabletNextOutput = new QAction(this);
|
||||
tabletNextOutput->setProperty("componentName", QStringLiteral("kwin"));
|
||||
tabletNextOutput->setText(i18n("Move the tablet to the next output"));
|
||||
tabletNextOutput->setObjectName(QStringLiteral("Move Tablet to Next Output"));
|
||||
KGlobalAccel::setGlobalShortcut(tabletNextOutput, QList<QKeySequence>());
|
||||
connect(tabletNextOutput, &QAction::triggered, this, &TabletInputRedirection::trackNextOutput);
|
||||
}
|
||||
|
||||
static TabletSeatV2Interface *findTabletSeat()
|
||||
{
|
||||
auto server = waylandServer();
|
||||
if (!server) {
|
||||
return nullptr;
|
||||
}
|
||||
TabletManagerV2Interface *manager = server->tabletManagerV2();
|
||||
return manager->seat(waylandServer()->seat());
|
||||
}
|
||||
|
||||
void TabletInputRedirection::integrateDevice(InputDevice *device)
|
||||
{
|
||||
TabletSeatV2Interface *tabletSeat = findTabletSeat();
|
||||
if (!tabletSeat) {
|
||||
qCCritical(KWIN_CORE) << "Could not find tablet seat";
|
||||
return;
|
||||
}
|
||||
|
||||
if (device->isTabletTool()) {
|
||||
tabletSeat->addTablet(device);
|
||||
}
|
||||
|
||||
if (device->isTabletPad()) {
|
||||
tabletSeat->addPad(device);
|
||||
}
|
||||
}
|
||||
|
||||
void TabletInputRedirection::removeDevice(InputDevice *device)
|
||||
{
|
||||
TabletSeatV2Interface *tabletSeat = findTabletSeat();
|
||||
if (tabletSeat) {
|
||||
tabletSeat->remove(device);
|
||||
} else {
|
||||
qCCritical(KWIN_CORE) << "Could not find tablet to remove" << device->name();
|
||||
}
|
||||
}
|
||||
|
||||
void TabletInputRedirection::trackNextOutput()
|
||||
{
|
||||
const auto outputs = workspace()->outputs();
|
||||
if (outputs.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int tabletToolCount = 0;
|
||||
InputDevice *changedDevice = nullptr;
|
||||
const auto devices = input()->devices();
|
||||
for (const auto device : devices) {
|
||||
if (!device->isTabletTool()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tabletToolCount++;
|
||||
if (device->outputName().isEmpty()) {
|
||||
device->setOutputName(outputs.constFirst()->name());
|
||||
changedDevice = device;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto it = std::find_if(outputs.begin(), outputs.end(), [device](const auto &output) {
|
||||
return output->name() == device->outputName();
|
||||
});
|
||||
++it;
|
||||
auto nextOutput = it == outputs.end() ? outputs.first() : *it;
|
||||
device->setOutputName(nextOutput->name());
|
||||
changedDevice = device;
|
||||
}
|
||||
const QString message = tabletToolCount == 1 ? i18n("Tablet moved to %1", changedDevice->outputName()) : i18n("Tablets switched outputs");
|
||||
OSD::show(message, QStringLiteral("input-tablet"), 5000);
|
||||
}
|
||||
|
||||
void TabletInputRedirection::ensureTabletTool(InputDeviceTabletTool *device)
|
||||
{
|
||||
TabletSeatV2Interface *tabletSeat = findTabletSeat();
|
||||
if (tabletSeat->tool(device)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TabletToolV2Interface *tool = tabletSeat->addTool(device);
|
||||
|
||||
const auto cursor = new SurfaceCursor(tool);
|
||||
Cursors::self()->addCursor(cursor);
|
||||
m_cursorByTool[device] = cursor;
|
||||
}
|
||||
|
||||
void TabletInputRedirection::tabletToolAxisEvent(const QPointF &pos, qreal pressure, qreal xTilt, qreal yTilt, qreal rotation, qreal distance, bool tipDown, bool tipNear, InputDeviceTabletTool *tool, std::chrono::microseconds time, InputDevice *device)
|
||||
{
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ensureTabletTool(tool);
|
||||
|
||||
m_lastPosition = pos;
|
||||
m_cursorByTool[tool]->setPos(pos);
|
||||
|
||||
update();
|
||||
workspace()->setActiveOutput(pos);
|
||||
|
||||
// TODO: Not correct, but it should work fine. In long term, we need to stop using QTabletEvent.
|
||||
const QPointingDevice *dev = QPointingDevice::primaryPointingDevice();
|
||||
const auto button = tipDown ? Qt::LeftButton : Qt::NoButton;
|
||||
TabletEvent ev(QEvent::TabletMove, dev, pos, pos, pressure,
|
||||
xTilt, yTilt,
|
||||
0, // tangentialPressure
|
||||
rotation,
|
||||
distance,
|
||||
Qt::NoModifier, button, button, tool, device);
|
||||
ev.setTimestamp(std::chrono::duration_cast<std::chrono::milliseconds>(time).count());
|
||||
|
||||
input()->processSpies(std::bind(&InputEventSpy::tabletToolAxisEvent, std::placeholders::_1, &ev));
|
||||
input()->processFilters(std::bind(&InputEventFilter::tabletToolAxisEvent, std::placeholders::_1, &ev));
|
||||
input()->setLastInputHandler(this);
|
||||
}
|
||||
|
||||
void TabletInputRedirection::tabletToolProximityEvent(const QPointF &pos, qreal pressure, qreal xTilt, qreal yTilt, qreal rotation, qreal distance, bool tipDown, bool tipNear, InputDeviceTabletTool *tool, std::chrono::microseconds time, InputDevice *device)
|
||||
{
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ensureTabletTool(tool);
|
||||
|
||||
m_lastPosition = pos;
|
||||
if (tipNear) {
|
||||
m_cursorByTool[tool]->setPos(pos);
|
||||
}
|
||||
|
||||
update();
|
||||
workspace()->setActiveOutput(pos);
|
||||
|
||||
// TODO: Not correct, but it should work fine. In long term, we need to stop using QTabletEvent.
|
||||
const QPointingDevice *dev = QPointingDevice::primaryPointingDevice();
|
||||
const auto button = tipDown ? Qt::LeftButton : Qt::NoButton;
|
||||
TabletEvent ev(tipNear ? QEvent::TabletEnterProximity : QEvent::TabletLeaveProximity, dev, pos, pos, pressure,
|
||||
xTilt, yTilt,
|
||||
0, // tangentialPressure
|
||||
rotation,
|
||||
distance,
|
||||
Qt::NoModifier, button, button, tool, device);
|
||||
ev.setTimestamp(std::chrono::duration_cast<std::chrono::milliseconds>(time).count());
|
||||
|
||||
input()->processSpies(std::bind(&InputEventSpy::tabletToolProximityEvent, std::placeholders::_1, &ev));
|
||||
input()->processFilters(std::bind(&InputEventFilter::tabletToolProximityEvent, std::placeholders::_1, &ev));
|
||||
input()->setLastInputHandler(this);
|
||||
}
|
||||
|
||||
void TabletInputRedirection::tabletToolTipEvent(const QPointF &pos, qreal pressure, qreal xTilt, qreal yTilt, qreal rotation, qreal distance, bool tipDown, bool tipNear, InputDeviceTabletTool *tool, std::chrono::microseconds time, InputDevice *device)
|
||||
{
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ensureTabletTool(tool);
|
||||
|
||||
m_lastPosition = pos;
|
||||
if (tipDown) {
|
||||
m_cursorByTool[tool]->setPos(pos);
|
||||
}
|
||||
|
||||
update();
|
||||
workspace()->setActiveOutput(pos);
|
||||
|
||||
// TODO: Not correct, but it should work fine. In long term, we need to stop using QTabletEvent.
|
||||
const QPointingDevice *dev = QPointingDevice::primaryPointingDevice();
|
||||
const auto button = tipDown ? Qt::LeftButton : Qt::NoButton;
|
||||
TabletEvent ev(tipDown ? QEvent::TabletPress : QEvent::TabletRelease, dev, pos, pos, pressure,
|
||||
xTilt, yTilt,
|
||||
0, // tangentialPressure
|
||||
rotation,
|
||||
distance,
|
||||
Qt::NoModifier, button, button, tool, device);
|
||||
ev.setTimestamp(std::chrono::duration_cast<std::chrono::milliseconds>(time).count());
|
||||
|
||||
input()->processSpies(std::bind(&InputEventSpy::tabletToolTipEvent, std::placeholders::_1, &ev));
|
||||
input()->processFilters(std::bind(&InputEventFilter::tabletToolTipEvent, std::placeholders::_1, &ev));
|
||||
input()->setLastInputHandler(this);
|
||||
}
|
||||
|
||||
void KWin::TabletInputRedirection::tabletToolButtonEvent(uint button, bool isPressed, InputDeviceTabletTool *tool, std::chrono::microseconds time, InputDevice *device)
|
||||
{
|
||||
TabletToolButtonEvent event{
|
||||
.device = device,
|
||||
.button = button,
|
||||
.pressed = isPressed,
|
||||
.tool = tool,
|
||||
.time = time,
|
||||
};
|
||||
|
||||
ensureTabletTool(tool);
|
||||
|
||||
input()->processSpies(std::bind(&InputEventSpy::tabletToolButtonEvent, std::placeholders::_1, &event));
|
||||
input()->processFilters(std::bind(&InputEventFilter::tabletToolButtonEvent, std::placeholders::_1, &event));
|
||||
input()->setLastInputHandler(this);
|
||||
}
|
||||
|
||||
void KWin::TabletInputRedirection::tabletPadButtonEvent(uint button, bool isPressed, std::chrono::microseconds time, InputDevice *device)
|
||||
{
|
||||
TabletPadButtonEvent event{
|
||||
.device = device,
|
||||
.button = button,
|
||||
.pressed = isPressed,
|
||||
.time = time,
|
||||
};
|
||||
input()->processSpies(std::bind(&InputEventSpy::tabletPadButtonEvent, std::placeholders::_1, &event));
|
||||
input()->processFilters(std::bind(&InputEventFilter::tabletPadButtonEvent, std::placeholders::_1, &event));
|
||||
input()->setLastInputHandler(this);
|
||||
}
|
||||
|
||||
void KWin::TabletInputRedirection::tabletPadStripEvent(int number, int position, bool isFinger, std::chrono::microseconds time, InputDevice *device)
|
||||
{
|
||||
TabletPadStripEvent event{
|
||||
.device = device,
|
||||
.number = number,
|
||||
.position = position,
|
||||
.isFinger = isFinger,
|
||||
.time = time,
|
||||
};
|
||||
|
||||
input()->processSpies(std::bind(&InputEventSpy::tabletPadStripEvent, std::placeholders::_1, &event));
|
||||
input()->processFilters(std::bind(&InputEventFilter::tabletPadStripEvent, std::placeholders::_1, &event));
|
||||
input()->setLastInputHandler(this);
|
||||
}
|
||||
|
||||
void KWin::TabletInputRedirection::tabletPadRingEvent(int number, int position, bool isFinger, std::chrono::microseconds time, InputDevice *device)
|
||||
{
|
||||
TabletPadRingEvent event{
|
||||
.device = device,
|
||||
.number = number,
|
||||
.position = position,
|
||||
.isFinger = isFinger,
|
||||
.time = time,
|
||||
};
|
||||
|
||||
input()->processSpies(std::bind(&InputEventSpy::tabletPadRingEvent, std::placeholders::_1, &event));
|
||||
input()->processFilters(std::bind(&InputEventFilter::tabletPadRingEvent, std::placeholders::_1, &event));
|
||||
input()->setLastInputHandler(this);
|
||||
}
|
||||
|
||||
bool TabletInputRedirection::focusUpdatesBlocked()
|
||||
{
|
||||
return input()->isSelectingWindow();
|
||||
}
|
||||
|
||||
void TabletInputRedirection::cleanupDecoration(Decoration::DecoratedWindowImpl *old,
|
||||
Decoration::DecoratedWindowImpl *now)
|
||||
{
|
||||
disconnect(m_decorationGeometryConnection);
|
||||
m_decorationGeometryConnection = QMetaObject::Connection();
|
||||
|
||||
disconnect(m_decorationDestroyedConnection);
|
||||
m_decorationDestroyedConnection = QMetaObject::Connection();
|
||||
|
||||
if (old) {
|
||||
// send leave event to old decoration
|
||||
QHoverEvent event(QEvent::HoverLeave, QPointF(), QPointF());
|
||||
QCoreApplication::instance()->sendEvent(old->decoration(), &event);
|
||||
}
|
||||
if (!now) {
|
||||
// left decoration
|
||||
return;
|
||||
}
|
||||
|
||||
const auto pos = m_lastPosition - now->window()->pos();
|
||||
QHoverEvent event(QEvent::HoverEnter, pos, pos);
|
||||
QCoreApplication::instance()->sendEvent(now->decoration(), &event);
|
||||
now->window()->processDecorationMove(pos, m_lastPosition);
|
||||
|
||||
m_decorationGeometryConnection = connect(
|
||||
decoration()->window(), &Window::frameGeometryChanged, this, [this]() {
|
||||
// ensure maximize button gets the leave event when maximizing/restore a window, see BUG 385140
|
||||
const auto oldDeco = decoration();
|
||||
update();
|
||||
if (oldDeco && oldDeco == decoration() && !decoration()->window()->isInteractiveMove() && !decoration()->window()->isInteractiveResize()) {
|
||||
// position of window did not change, we need to send HoverMotion manually
|
||||
const QPointF p = m_lastPosition - decoration()->window()->pos();
|
||||
QHoverEvent event(QEvent::HoverMove, p, p);
|
||||
QCoreApplication::instance()->sendEvent(decoration()->decoration(), &event);
|
||||
}
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
|
||||
// if our decoration gets destroyed whilst it has focus, we pass focus on to the same client
|
||||
m_decorationDestroyedConnection = connect(now, &QObject::destroyed, this, &TabletInputRedirection::update, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void TabletInputRedirection::focusUpdate(Window *focusOld, Window *focusNow)
|
||||
{
|
||||
// This method is left blank intentionally.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_tablet_input.cpp"
|
||||
Reference in New Issue
Block a user