Add kwin full source tree, greeter login, zsh, pcid service, and build system improvements

This commit is contained in:
2026-04-26 22:31:07 +01:00
parent d4a6b356eb
commit 70a84cefee
3416 changed files with 1360518 additions and 10522 deletions
+403
View File
@@ -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"