diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -497,10 +497,13 @@ libinput/device.cpp libinput/events.cpp libinput/libinput_logging.cpp - udev.cpp touch_hide_cursor_spy.cpp internal_client.cpp xwl/xwayland_interface.cpp + toolkit/udev_context.cpp + toolkit/udev_device.cpp + toolkit/udev_enumerator.cpp + toolkit/udev_monitor.cpp ) include(ECMQtDeclareLoggingCategory) diff --git a/autotests/libinput/context_test.cpp b/autotests/libinput/context_test.cpp --- a/autotests/libinput/context_test.cpp +++ b/autotests/libinput/context_test.cpp @@ -20,7 +20,7 @@ #include "mock_libinput.h" #include "mock_udev.h" #include "../../libinput/context.h" -#include "../../udev.h" +#include "../../toolkit/udev_context.h" #include Q_LOGGING_CATEGORY(KWIN_CORE, "kwin_core", QtCriticalMsg) @@ -47,7 +47,7 @@ { // this test verifies that isValid is false if the setup fails // we create an Udev without a mockUdev - Udev u; + UdevContext u; QVERIFY(!(udev*)(u)); Context context(u); QVERIFY(!context.isValid()); @@ -73,7 +73,7 @@ // setup udev so that we can create a context udev::s_mockUdev = new udev; QVERIFY(udev::s_mockUdev); - Udev u; + UdevContext u; QVERIFY((udev*)(u)); Context context(u); QVERIFY(context.isValid()); diff --git a/autotests/libinput/mock_udev.cpp b/autotests/libinput/mock_udev.cpp --- a/autotests/libinput/mock_udev.cpp +++ b/autotests/libinput/mock_udev.cpp @@ -17,21 +17,31 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ -#include "../../udev.h" + #include "mock_udev.h" +#include "../toolkit/udev_context.h" + udev *udev::s_mockUdev = nullptr; namespace KWin { -Udev::Udev() +UdevContext::UdevContext() : m_udev(udev::s_mockUdev) { } -Udev::~Udev() +UdevContext::UdevContext(const UdevContext &other) = default; +UdevContext::UdevContext(UdevContext &&other) = default; +UdevContext::~UdevContext() = default; + +UdevContext &UdevContext::operator=(const UdevContext &other) = default; +UdevContext &UdevContext::operator=(UdevContext &&other) = default; + +UdevContext::operator udev*() const { + return m_udev; } } diff --git a/libinput/connection.cpp b/libinput/connection.cpp --- a/libinput/connection.cpp +++ b/libinput/connection.cpp @@ -25,9 +25,10 @@ #include "../screens.h" #endif #include "../logind.h" -#include "../udev.h" +#include "../toolkit/udev_context.h" #include "libinput_logging.h" + #include #include #include @@ -122,7 +123,7 @@ Connection *Connection::create(QObject *parent) { Q_ASSERT(!s_self); - static Udev s_udev; + static UdevContext s_udev; if (!s_udev.isValid()) { qCWarning(KWIN_LIBINPUT) << "Failed to initialize udev"; return nullptr; diff --git a/libinput/context.h b/libinput/context.h --- a/libinput/context.h +++ b/libinput/context.h @@ -25,7 +25,7 @@ namespace KWin { -class Udev; +class UdevContext; namespace LibInput { @@ -35,7 +35,7 @@ class Context { public: - Context(const Udev &udev); + Context(const UdevContext &udev); ~Context(); bool assignSeat(const char *seat); bool isValid() const { diff --git a/libinput/context.cpp b/libinput/context.cpp --- a/libinput/context.cpp +++ b/libinput/context.cpp @@ -21,7 +21,8 @@ #include "events.h" #include "libinput_logging.h" #include "../logind.h" -#include "../udev.h" + +#include "../toolkit/udev_context.h" #include #include @@ -53,7 +54,7 @@ } } -Context::Context(const Udev &udev) +Context::Context(const UdevContext &udev) : m_libinput(libinput_udev_create_context(&Context::s_interface, this, udev)) , m_suspended(false) { diff --git a/plugins/platforms/drm/CMakeLists.txt b/plugins/platforms/drm/CMakeLists.txt --- a/plugins/platforms/drm/CMakeLists.txt +++ b/plugins/platforms/drm/CMakeLists.txt @@ -1,12 +1,14 @@ set(DRM_SOURCES drm_backend.cpp + drm_buffer.cpp + drm_device_manager.cpp + drm_device.cpp + drm_inputeventfilter.cpp drm_object.cpp drm_object_connector.cpp drm_object_crtc.cpp drm_object_plane.cpp drm_output.cpp - drm_buffer.cpp - drm_inputeventfilter.cpp logging.cpp scene_qpainter_drm_backend.cpp screens_drm.cpp diff --git a/plugins/platforms/drm/drm_backend.h b/plugins/platforms/drm/drm_backend.h --- a/plugins/platforms/drm/drm_backend.h +++ b/plugins/platforms/drm/drm_backend.h @@ -56,7 +56,7 @@ namespace KWin { -class Udev; +class UdevContext; class UdevMonitor; class DrmOutput; @@ -173,10 +173,10 @@ QByteArray generateOutputConfigurationUuid() const; DrmOutput *findOutput(quint32 connector); DrmOutput *findOutput(const QByteArray &uuid); - QScopedPointer m_udev; - QScopedPointer m_udevMonitor; + QScopedPointer m_udevContext; + UdevMonitor *m_udevMonitor; int m_fd = -1; - int m_drmId = 0; + QString m_drmId; // all crtcs QVector m_crtcs; // all connectors diff --git a/plugins/platforms/drm/drm_backend.cpp b/plugins/platforms/drm/drm_backend.cpp --- a/plugins/platforms/drm/drm_backend.cpp +++ b/plugins/platforms/drm/drm_backend.cpp @@ -29,15 +29,19 @@ #include "main.h" #include "scene_qpainter_drm_backend.h" #include "screens_drm.h" -#include "udev.h" #include "wayland_server.h" #if HAVE_GBM #include "egl_gbm_backend.h" #include #endif #if HAVE_EGL_STREAMS #include "egl_stream_backend.h" #endif + +#include "toolkit/udev_context.h" +#include "toolkit/udev_enumerator.h" +#include "toolkit/udev_monitor.h" + // KWayland #include #include @@ -73,8 +77,8 @@ DrmBackend::DrmBackend(QObject *parent) : Platform(parent) - , m_udev(new Udev) - , m_udevMonitor(m_udev->monitor()) + , m_udevContext(new UdevContext()) + , m_udevMonitor(new UdevMonitor(m_udevContext.data())) , m_dpmsFilter() { #if HAVE_EGL_STREAMS @@ -250,15 +254,32 @@ } } +static UdevDevice findPrimaryDevice(const UdevContext &context) +{ + UdevEnumerator enumerator(context); + enumerator.matchSeat(QStringLiteral("seat0")); + enumerator.matchSubsystem(QStringLiteral("drm")); + enumerator.matchSysfsName(QStringLiteral("card[0-9]*")); + + const UdevDeviceList devices = enumerator.scan(); + for (const UdevDevice &device : devices) { + if (device.types() & UdevDevice::PrimaryGpu) { + return device; + } + } + + return UdevDevice(); +} + void DrmBackend::openDrm() { connect(LogindIntegration::self(), &LogindIntegration::sessionActiveChanged, this, &DrmBackend::activate); - UdevDevice::Ptr device = m_udev->primaryGpu(); - if (!device) { + UdevDevice device = findPrimaryDevice(*m_udevContext.data()); + if (!device.isValid()) { qCWarning(KWIN_DRM) << "Did not find a GPU"; return; } - m_devNode = device->devNode(); + m_devNode = device.deviceNode().toUtf8(); int fd = LogindIntegration::self()->takeDevice(m_devNode.constData()); if (fd < 0) { qCWarning(KWIN_DRM) << "failed to open drm device at" << m_devNode; @@ -279,7 +300,7 @@ drmHandleEvent(m_fd, &e); } ); - m_drmId = device->sysNum(); + m_drmId = device.sysfsNumber(); // trying to activate Atomic Mode Setting (this means also Universal Planes) if (!qEnvironmentVariableIsSet("KWIN_DRM_NO_AMS")) { @@ -358,28 +379,24 @@ // setup udevMonitor if (m_udevMonitor) { - m_udevMonitor->filterSubsystemDevType("drm"); - const int fd = m_udevMonitor->fd(); - if (fd != -1) { - QSocketNotifier *notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this); - connect(notifier, &QSocketNotifier::activated, this, - [this] { - auto device = m_udevMonitor->getDevice(); - if (!device) { - return; - } - if (device->sysNum() != m_drmId) { - return; - } - if (device->hasProperty("HOTPLUG", "1")) { - qCDebug(KWIN_DRM) << "Received hot plug event for monitored drm device"; - updateOutputs(); - updateCursor(); - } + m_udevMonitor->filterBySubsystem(QStringLiteral("drm")); + m_udevMonitor->enable(); + + connect(m_udevMonitor, &UdevMonitor::deviceChanged, this, + [this](const UdevDevice &device) { + if (device.sysfsNumber() != m_drmId) { + return; } - ); - m_udevMonitor->enable(); - } + + if (device.property(QStringLiteral("HOTPLUT")) != "1") { + return; + } + + qCDebug(KWIN_DRM) << "Received hot plug event for monitored drm device"; + updateOutputs(); + updateCursor(); + } + ); } setReady(true); } diff --git a/plugins/platforms/drm/drm_device.h b/plugins/platforms/drm/drm_device.h new file mode 100644 --- /dev/null +++ b/plugins/platforms/drm/drm_device.h @@ -0,0 +1,132 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2019 Vlad Zagorodniy + +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 . +*********************************************************************/ + +#pragma once + +#include + +namespace KWin +{ + +/** + * The DrmDevice class represents a DRM device. + * + * @todo Write documentation. + **/ +class DrmDevice : public QObject +{ + Q_OBJECT + +public: + explicit DrmDevice(const QString &fileName, QObject *parent = nullptr); + ~DrmDevice() override; + + /** + * Returns @c true if this device is valid. + **/ + bool isValid() const; + + /** + * Returns the file name of this device, e.g. /dev/dri/card0. + **/ + QString fileName() const; + + /** + * Returns the file descriptor of this device. + **/ + int fd() const; + + /** + * This enum type is used to specify device capabilities. + **/ + enum DeviceCapability { + /** + * This device supports dumb buffers. + **/ + DeviceCapabilityDumbBuffer, + /** + * This device can export buffers over dma-buf. + **/ + DeviceCapabilityExportBuffer, + /** + * This device can import buffer over dma-buf. + **/ + DeviceCapabilityImportBuffer, + /** + * This device supports buffer modifiers. + **/ + DeviceCapabilityBufferModifier, + }; + + /** + * Checks whether this device supports given capability. + * + * If the capability is supported, @c true is returned. Otherwise @c false. + **/ + bool supports(DeviceCapability capability) const; + + /** + * This enum type is used to specify capabilities requested by the client. + **/ + enum ClientCapability { + /** + * The client would like to use atomic modesetting API. + **/ + ClientCapabilityAtomic, + /** + * The client would like to have access to planes. + **/ + ClientCapabilityUniversalPlanes, + }; + + /** + * Enables the given client capability. + * + * If the device couldn't enable the given capability, @c false is returned. + **/ + bool enable(ClientCapability capability); + + /** + * Waits until this device becomes idle. + * + * If the device is already idle, then this method will return immediately, + * otherwise it will run the event loop until the device becomes idle. + **/ + void waitIdle(); + +private Q_SLOTS: + void dispatchEvents(); + +private: + QString m_fileName; + + int m_fd = -1; + bool m_supportsDumbBuffer = false; + bool m_supportsExportBuffer = false; + bool m_supportsImportBuffer = false; + bool m_supportsBufferModifier = false; + bool m_isValid = false; + + Q_DISABLE_COPY(DrmDevice) +}; + +typedef QVector DrmDeviceList; + +} // namespace KWin diff --git a/plugins/platforms/drm/drm_device.cpp b/plugins/platforms/drm/drm_device.cpp new file mode 100644 --- /dev/null +++ b/plugins/platforms/drm/drm_device.cpp @@ -0,0 +1,141 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2019 Vlad Zagorodniy + +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 . +*********************************************************************/ + +#include "drm_device.h" +#include "drm_pointer.h" + +#include "logind.h" + +#include + +namespace KWin +{ + +static uint64_t queryCapability(int fd, uint32_t capability) +{ + uint64_t value = 0; + + if (drmGetCap(fd, capability, &value)) { + return 0; + } + + return value; +} + +DrmDevice::DrmDevice(const QString &fileName, QObject *parent) + : QObject(parent) + , m_fileName(fileName) +{ + m_fd = LogindIntegration::self()->takeDevice(fileName.toUtf8()); + if (m_fd < 0) { + return; + } + + DrmScopedPointer resources(drmModeGetResources(m_fd)); + if (!resources) { + return; + } + + m_supportsDumbBuffer = queryCapability(m_fd, DRM_CAP_DUMB_BUFFER); + m_supportsExportBuffer = queryCapability(m_fd, DRM_CAP_PRIME) & DRM_PRIME_CAP_EXPORT; + m_supportsImportBuffer = queryCapability(m_fd, DRM_CAP_PRIME) & DRM_PRIME_CAP_IMPORT; + m_supportsBufferModifier = queryCapability(m_fd, DRM_CAP_ADDFB2_MODIFIERS); + + m_isValid = true; +} + +DrmDevice::~DrmDevice() +{ + if (m_fd != -1) { + LogindIntegration::self()->releaseDevice(m_fd); + } +} + +bool DrmDevice::isValid() const +{ + return m_isValid; +} + +QString DrmDevice::fileName() const +{ + return m_fileName; +} + +int DrmDevice::fd() const +{ + return m_fd; +} + +bool DrmDevice::supports(DeviceCapability capability) const +{ + switch (capability) { + case DeviceCapabilityDumbBuffer: + return m_supportsDumbBuffer; + case DeviceCapabilityExportBuffer: + return m_supportsExportBuffer; + case DeviceCapabilityImportBuffer: + return m_supportsImportBuffer; + case DeviceCapabilityBufferModifier: + return m_supportsBufferModifier; + default: + Q_UNREACHABLE(); + } +} + +bool DrmDevice::enable(ClientCapability capability) +{ + switch (capability) { + case ClientCapabilityAtomic: + return !drmSetClientCap(m_fd, DRM_CLIENT_CAP_ATOMIC, 1); + case ClientCapabilityUniversalPlanes: + return !drmSetClientCap(m_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); + default: + Q_UNREACHABLE(); + } +} + +static void deviceEventHandler(int, + unsigned int sequence, + unsigned int tv_sec, + unsigned int tv_usec, + unsigned int crtc_id, + void* user_data) +{ + Q_UNUSED(sequence) + Q_UNUSED(tv_sec) + Q_UNUSED(tv_usec) + Q_UNUSED(crtc_id) + Q_UNUSED(user_data) +} + +void DrmDevice::dispatchEvents() +{ + drmEventContext context = {}; + context.version = 3; + context.page_flip_handler2 = deviceEventHandler; + + drmHandleEvent(m_fd, &context); +} + +void DrmDevice::waitIdle() +{ +} + +} // namespace KWin diff --git a/plugins/platforms/drm/drm_device_manager.h b/plugins/platforms/drm/drm_device_manager.h new file mode 100644 --- /dev/null +++ b/plugins/platforms/drm/drm_device_manager.h @@ -0,0 +1,74 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2019 Vlad Zagorodniy + +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 . +*********************************************************************/ + +#pragma once + +#include "drm_device.h" + +namespace KWin +{ + +class UdevDevice; + +/** + * The DrmDeviceManager class is responsible for managing DRM devices. + * + * @todo More documentation. + **/ +class DrmDeviceManager : public QObject +{ + Q_OBJECT + +public: + explicit DrmDeviceManager(QObject *parent = nullptr); + ~DrmDeviceManager() override; + + /** + * Returns @c true if the DRM device manager is valid. + **/ + bool isValid() const; + + /** + * Returns all managed DRM devices. + **/ + DrmDeviceList devices() const; + + /** + * Returns the primary DRM device. + * + * @todo Write some stuff about PRIME. + **/ + DrmDevice *master() const; + +private Q_SLOTS: + void slotDeviceAdded(const UdevDevice &device); + void slotDeviceRemoved(const UdevDevice &device); + void slotDeviceChanged(const UdevDevice &device); + +private: + DrmDevice *findDevice(const UdevDevice &udev); + + DrmDeviceList m_devices; + DrmDevice *m_master = nullptr; + + Q_DISABLE_COPY(DrmDeviceManager) +}; + +} // namespace KWin diff --git a/plugins/platforms/drm/drm_device_manager.cpp b/plugins/platforms/drm/drm_device_manager.cpp new file mode 100644 --- /dev/null +++ b/plugins/platforms/drm/drm_device_manager.cpp @@ -0,0 +1,159 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2019 Vlad Zagorodniy + +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 . +*********************************************************************/ + +#include "drm_device_manager.h" +#include "drm_device.h" + +#include "toolkit/udev_enumerator.h" +#include "toolkit/udev_monitor.h" + +#include + +namespace KWin +{ + +DrmDeviceManager::DrmDeviceManager(QObject *parent) + : QObject(parent) +{ + UdevContext context; + + UdevEnumerator enumerator(context); + enumerator.matchSeat(QStringLiteral("seat0")); + enumerator.matchSubsystem(QStringLiteral("drm")); + enumerator.matchSysfsName(QStringLiteral("card[0-9]*")); + + const UdevDeviceList devices = enumerator.scan(); + + for (const UdevDevice &device : devices) { + if (device.types() & UdevDevice::PrimaryGpu) { + slotDeviceAdded(device); + } + if (!m_devices.isEmpty()) { + break; + } + } + + m_master = m_devices.first(); + + for (const UdevDevice &device : devices) { + if (!(device.types() & UdevDevice::PrimaryGpu)) { + slotDeviceAdded(device); + } + } + + UdevMonitor *monitor = new UdevMonitor(&context, this); + monitor->filterBySubsystem(QStringLiteral("drm")); + monitor->enable(); + + connect(monitor, &UdevMonitor::deviceAdded, this, &DrmDeviceManager::slotDeviceAdded); + connect(monitor, &UdevMonitor::deviceRemoved, this, &DrmDeviceManager::slotDeviceRemoved); + connect(monitor, &UdevMonitor::deviceChanged, this, &DrmDeviceManager::slotDeviceChanged); +} + +DrmDeviceManager::~DrmDeviceManager() +{ + // for (DrmDevice *device : m_devices) { + // device->freeze(); + // } + + for (DrmDevice *device : m_devices) { + device->waitIdle(); + } +} + +bool DrmDeviceManager::isValid() const +{ + return !m_devices.isEmpty(); +} + +DrmDeviceList DrmDeviceManager::devices() const +{ + return m_devices; +} + +DrmDevice *DrmDeviceManager::master() const +{ + return m_master; +} + +void DrmDeviceManager::slotDeviceAdded(const UdevDevice &udevDevice) +{ + if (!(udevDevice.types() & UdevDevice::Gpu)) { + return; + } + + const QString fileName = udevDevice.deviceNode(); + if (fileName.isEmpty()) { + return; + } + + QScopedPointer device(new DrmDevice(fileName, this)); + + if (m_master) { + if (!m_master->supports(DrmDevice::DeviceCapabilityExportBuffer)) { + return; + } + if (!device->supports(DrmDevice::DeviceCapabilityImportBuffer)) { + return; + } + } + + m_devices << device.take(); +} + +void DrmDeviceManager::slotDeviceRemoved(const UdevDevice &udevDevice) +{ + if (!(udevDevice.types() & UdevDevice::Gpu)) { + return; + } + + DrmDevice *device = findDevice(udevDevice); + if (!device) { + return; + } + + m_devices.removeOne(device); +} + +void DrmDeviceManager::slotDeviceChanged(const UdevDevice &udevDevice) +{ + if (!(udevDevice.types() & UdevDevice::Gpu)) { + return; + } + + DrmDevice *device = findDevice(udevDevice); + if (!device) { + return; + } + + device->waitIdle(); +} + +DrmDevice *DrmDeviceManager::findDevice(const UdevDevice &udev) +{ + for (DrmDevice *device : m_devices) { + if (device->fileName() == udev.deviceNode()) { + return device; + } + } + return nullptr; +} + +} // namespace KWin diff --git a/plugins/platforms/fbdev/fb_backend.cpp b/plugins/platforms/fbdev/fb_backend.cpp --- a/plugins/platforms/fbdev/fb_backend.cpp +++ b/plugins/platforms/fbdev/fb_backend.cpp @@ -25,7 +25,10 @@ #include "scene_qpainter_fb_backend.h" #include "outputscreens.h" #include "virtual_terminal.h" -#include "udev.h" + +#include "toolkit/udev_context.h" +#include "toolkit/udev_enumerator.h" + // system #include #include @@ -80,12 +83,32 @@ VirtualTerminal::create(this); } +static UdevDevice findPrimaryDevice() +{ + UdevContext context; + + UdevEnumerator enumerator(context); + enumerator.matchSeat(QStringLiteral("seat0")); + enumerator.matchSubsystem(QStringLiteral("graphics")); + enumerator.matchSysfsName(QStringLiteral("fb[0-9]*")); + + const UdevDeviceList devices = enumerator.scan(); + for (const UdevDevice &device : devices) { + if (device.types() & UdevDevice::PrimaryFrameBuffer) { + return device; + } + } + + return UdevDevice(); +} + void FramebufferBackend::openFrameBuffer() { VirtualTerminal::self()->init(); QString framebufferDevice = deviceIdentifier().constData(); if (framebufferDevice.isEmpty()) { - framebufferDevice = QString(Udev().primaryFramebuffer()->devNode()); + const UdevDevice device = findPrimaryDevice(); + framebufferDevice = device.deviceNode(); } int fd = LogindIntegration::self()->takeDevice(framebufferDevice.toUtf8().constData()); qCDebug(KWIN_FB) << "Using frame buffer device:" << framebufferDevice; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -26,7 +26,8 @@ ${KWIN_SOURCE_DIR}/libinput/events.cpp ${KWIN_SOURCE_DIR}/libinput/libinput_logging.cpp ${KWIN_SOURCE_DIR}/logind.cpp - ${KWIN_SOURCE_DIR}/udev.cpp + ${KWIN_SOURCE_DIR}/toolkit/udev_context.cpp + ${KWIN_SOURCE_DIR}/toolkit/udev_device.cpp ) add_executable(libinputtest ${libinputtest_SRCS}) add_definitions(-DKWIN_BUILD_TESTING) diff --git a/toolkit/udev_context.h b/toolkit/udev_context.h new file mode 100644 --- /dev/null +++ b/toolkit/udev_context.h @@ -0,0 +1,79 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2019 Vlad Zagorodniy + +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 . +*********************************************************************/ + +#pragma once + +#include "kwin_export.h" + +#include + +#include + +namespace KWin +{ + +class UdevDevice; + +/** + * @todo Write documentation. + **/ +class KWIN_EXPORT UdevContext +{ +public: + explicit UdevContext(); + UdevContext(const UdevContext &other); + UdevContext(UdevContext &&other); + ~UdevContext(); + + UdevContext &operator=(const UdevContext &other); + UdevContext &operator=(UdevContext &&other); + + /** + * @todo Write documentation. + **/ + bool isValid() const; + + /** + * @todo Write documentation. + **/ + UdevDevice deviceFromFileName(const QString &fileName) const; + + /** + * @todo Write documentation. + **/ + UdevDevice deviceFromId(const QString &id) const; + + /** + * @todo Write documentation. + **/ + UdevDevice deviceFromSubSystemAndName(const QString &subSystem, const QString &name) const; + + /** + * @todo Write documentation. + **/ + UdevDevice deviceFromSysfsPath(const QString &path) const; + + operator udev*() const; + +private: + udev *m_udev; +}; + +} // namespace KWin diff --git a/toolkit/udev_context.cpp b/toolkit/udev_context.cpp new file mode 100644 --- /dev/null +++ b/toolkit/udev_context.cpp @@ -0,0 +1,134 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2019 Vlad Zagorodniy + +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 . +*********************************************************************/ + +#include "udev_context.h" +#include "udev_device.h" + +#include + +namespace KWin +{ + +UdevContext::UdevContext() + : m_udev(udev_new()) +{ +} + +UdevContext::UdevContext(const UdevContext &other) + : m_udev(udev_ref(other.m_udev)) +{ +} + +UdevContext::UdevContext(UdevContext &&other) + : m_udev(std::exchange(other.m_udev, nullptr)) +{ +} + +UdevContext::~UdevContext() +{ + if (m_udev) { + udev_unref(m_udev); + } +} + +UdevContext &UdevContext::operator=(const UdevContext &other) +{ + if (m_udev) { + udev_unref(m_udev); + } + + if (other.m_udev) { + m_udev = udev_ref(other.m_udev); + } else { + m_udev = nullptr; + } + + return *this; +} + +UdevContext &UdevContext::operator=(UdevContext &&other) +{ + if (m_udev) { + udev_unref(m_udev); + } + + m_udev = std::exchange(other.m_udev, nullptr); + + return *this; +} + +bool UdevContext::isValid() const +{ + return m_udev; +} + +UdevDevice UdevContext::deviceFromFileName(const QString &fileName) const +{ + if (!m_udev) { + return nullptr; + } + + struct stat statBuf; + if (stat(fileName.toUtf8(), &statBuf)) { + return nullptr; + } + + char type; + if (S_ISBLK(statBuf.st_mode)) { + type = 'b'; + } else if (S_ISCHR(statBuf.st_mode)) { + type = 'c'; + } else { + return nullptr; + } + + return udev_device_new_from_devnum(m_udev, type, statBuf.st_rdev); +} + +UdevDevice UdevContext::deviceFromId(const QString &id) const +{ + if (m_udev) { + return udev_device_new_from_device_id(m_udev, id.toUtf8()); + } + return nullptr; +} + +UdevDevice UdevContext::deviceFromSubSystemAndName(const QString &subSystem, const QString &name) const +{ + if (m_udev) { + return udev_device_new_from_subsystem_sysname(m_udev, subSystem.toUtf8(), name.toUtf8()); + } + return nullptr; +} + +UdevDevice UdevContext::deviceFromSysfsPath(const QString &path) const +{ + if (m_udev) { + return udev_device_new_from_syspath(m_udev, path.toUtf8()); + } + return nullptr; +} + +UdevContext::operator udev*() const +{ + return m_udev; +} + +} // namespace KWin diff --git a/toolkit/udev_device.h b/toolkit/udev_device.h new file mode 100644 --- /dev/null +++ b/toolkit/udev_device.h @@ -0,0 +1,151 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2019 Vlad Zagorodniy + +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 . +*********************************************************************/ + +#pragma once + +#include "kwin_export.h" + +#include + +#include + +namespace KWin +{ + +/** + * @todo Write documentation. + **/ +class KWIN_EXPORT UdevDevice +{ +public: + /** + * This enum type is used to specify type of this udev device. + **/ + enum Type { + /** + * @todo Write documentation. + **/ + Unknown, + /** + * @todo Write documentation + **/ + Gpu = 1 << 0, + /** + * @todo Write documentation. + **/ + PrimaryGpu = 1 << 1, + /** + * @todo Write documentation. + **/ + FrameBuffer = 1 << 2, + /** + * @todo Write documentation. + **/ + PrimaryFrameBuffer = 1 << 3, + }; + Q_DECLARE_FLAGS(Types, Type) + + explicit UdevDevice(); + UdevDevice(const UdevDevice &other); + UdevDevice(UdevDevice &&other); + ~UdevDevice(); + + UdevDevice &operator=(const UdevDevice &other); + UdevDevice &operator=(UdevDevice &&other); + + /** + * @todo Write documentation. + **/ + bool isValid() const; + + /** + * @todo Write documentation. + **/ + Types types() const; + + /** + * @todo Write documentation. + **/ + UdevDevice parent() const; + + /** + * @todo Write documentation. + **/ + QString sysfsPath() const; + + /** + * @todo Write documentation. + **/ + QString sysfsName() const; + + /** + * @todo Write documentation. + **/ + QString sysfsNumber() const; + + /** + * @todo Write documentation. + **/ + QString devicePath() const; + + /** + * @todo Write documentation. + **/ + QString deviceNode() const; + + /** + * @todo Write documentation. + **/ + dev_t deviceNumber() const; + + /** + * @todo Write documentation. + **/ + QString driver() const; + + /** + * @todo Write documentation. + **/ + QString seat() const; + + /** + * @todo Write documentation. + **/ + QString subsystem() const; + + /** + * @todo Write documentation. + **/ + QByteArray property(const QString &name) const; + + operator udev_device*() const; + +private: + UdevDevice(udev_device *device); + + udev_device *m_device; + + friend class UdevContext; + friend class UdevMonitor; +}; + +typedef QVector UdevDeviceList; + +} // namespace KWin diff --git a/toolkit/udev_device.cpp b/toolkit/udev_device.cpp new file mode 100644 --- /dev/null +++ b/toolkit/udev_device.cpp @@ -0,0 +1,224 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2019 Vlad Zagorodniy + +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 . +*********************************************************************/ + +#include "udev_device.h" +#include "udev_context.h" + +namespace KWin +{ + +UdevDevice::UdevDevice() + : m_device(nullptr) +{ +} + +UdevDevice::UdevDevice(udev_device *device) + : m_device(device) +{ +} + +UdevDevice::UdevDevice(const UdevDevice &other) + : m_device(udev_device_ref(other.m_device)) +{ +} + +UdevDevice::UdevDevice(UdevDevice &&other) + : m_device(std::exchange(other.m_device, nullptr)) +{ +} + +UdevDevice::~UdevDevice() +{ + if (m_device) { + udev_device_unref(m_device); + } +} + +UdevDevice &UdevDevice::operator=(const UdevDevice &other) +{ + if (m_device) { + udev_device_unref(m_device); + } + + if (other.m_device) { + m_device = udev_device_ref(other.m_device); + } else { + m_device = nullptr; + } + + return *this; +} + +UdevDevice &UdevDevice::operator=(UdevDevice &&other) +{ + if (m_device) { + udev_device_unref(m_device); + } + + m_device = std::exchange(other.m_device, nullptr); + + return *this; +} + +bool UdevDevice::isValid() const +{ + return m_device; +} + +static bool isPrimaryDevice(udev_device *device) +{ + udev_device *pci = udev_device_get_parent_with_subsystem_devtype(device, "pci", nullptr); + if (!pci) { + return false; + } + + const char *value = udev_device_get_sysattr_value(pci, "boot_vga"); + if (!value) { + return false; + } + + return value == QByteArrayLiteral("1"); +} + +UdevDevice::Types UdevDevice::types() const +{ + if (!m_device) { + return Unknown; + } + + Types types = Unknown; + + if (subsystem() == QLatin1String("drm")) { + if (isPrimaryDevice(m_device)) { + types |= PrimaryGpu; + } + types |= Gpu; + } + + if (subsystem() == QLatin1String("graphics")) { + if (isPrimaryDevice(m_device)) { + types |= PrimaryFrameBuffer; + } + types |= FrameBuffer; + } + + return types; +} + +UdevDevice UdevDevice::parent() const +{ + if (m_device) { + return udev_device_get_parent(m_device); + } + return nullptr; +} + +QString UdevDevice::sysfsPath() const +{ + if (m_device) { + return QString::fromUtf8(udev_device_get_syspath(m_device)); + } + return QString(); +} + +QString UdevDevice::sysfsName() const +{ + if (m_device) { + return QString::fromUtf8(udev_device_get_sysname(m_device)); + } + return QString(); +} + +QString UdevDevice::sysfsNumber() const +{ + if (m_device) { + return QString::fromUtf8(udev_device_get_sysnum(m_device)); + } + return QString(); +} + +QString UdevDevice::devicePath() const +{ + if (m_device) { + return QString::fromUtf8(udev_device_get_devpath(m_device)); + } + return QString(); +} + +QString UdevDevice::deviceNode() const +{ + if (m_device) { + return QString::fromUtf8(udev_device_get_devnode(m_device)); + } + return QString(); +} + +dev_t UdevDevice::deviceNumber() const +{ + if (m_device) { + return udev_device_get_devnum(m_device); + } + return -1; +} + +QString UdevDevice::driver() const +{ + if (m_device) { + return QString::fromUtf8(udev_device_get_driver(m_device)); + } + return QString(); +} + +QString UdevDevice::seat() const +{ + if (!m_device) { + return QString(); + } + + QString seat = QString::fromLatin1(udev_device_get_property_value(m_device, "ID_SEAT")); + if (seat.isEmpty()) { + return QStringLiteral("seat0"); + } + + return seat; +} + +QString UdevDevice::subsystem() const +{ + if (m_device) { + return QString::fromUtf8(udev_device_get_subsystem(m_device)); + } + return QString(); +} + +QByteArray UdevDevice::property(const QString &name) const +{ + if (m_device) { + return udev_device_get_property_value(m_device, name.toUtf8()); + } + return QByteArray(); +} + +UdevDevice::operator udev_device*() const +{ + return m_device; +} + +} // namespace KWin diff --git a/toolkit/udev_enumerator.h b/toolkit/udev_enumerator.h new file mode 100644 --- /dev/null +++ b/toolkit/udev_enumerator.h @@ -0,0 +1,76 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2019 Vlad Zagorodniy + +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 . +*********************************************************************/ + +#pragma once + +#include "kwin_export.h" + +#include "udev_context.h" +#include "udev_device.h" + +namespace KWin +{ + +/** + * @todo Write documentation. + **/ +class KWIN_EXPORT UdevEnumerator +{ +public: + explicit UdevEnumerator(const UdevContext &context); + UdevEnumerator(const UdevEnumerator &other); + UdevEnumerator(UdevEnumerator &&other); + ~UdevEnumerator(); + + UdevEnumerator &operator=(const UdevEnumerator &other); + UdevEnumerator &operator=(UdevEnumerator &&other); + + /** + * @todo Write documentation. + **/ + bool isValid() const; + + /** + * @todo Write documentation. + **/ + void matchSeat(const QString &seat); + + /** + * @todo Write documentation. + **/ + void matchSubsystem(const QString &subsystem); + + /** + * @todo Write documentation. + **/ + void matchSysfsName(const QString &name); + + /** + * @todo Write documentation. + **/ + UdevDeviceList scan() const; + +private: + UdevContext m_context; + udev_enumerate *m_enumerate; + QString m_seat; +}; + +} // namespace KWin diff --git a/toolkit/udev_enumerator.cpp b/toolkit/udev_enumerator.cpp new file mode 100644 --- /dev/null +++ b/toolkit/udev_enumerator.cpp @@ -0,0 +1,136 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2019 Vlad Zagorodniy + +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 . +*********************************************************************/ + +#include "udev_enumerator.h" + +#include + +namespace KWin +{ + +UdevEnumerator::UdevEnumerator(const UdevContext &context) + : m_context(context) + , m_enumerate(udev_enumerate_new(context)) +{ +} + +UdevEnumerator::UdevEnumerator(const UdevEnumerator &other) + : m_context(other.m_context) + , m_enumerate(udev_enumerate_ref(other.m_enumerate)) +{ +} + +UdevEnumerator::UdevEnumerator(UdevEnumerator &&other) + : m_context(std::move(other.m_context)) + , m_enumerate(std::exchange(other.m_enumerate, nullptr)) +{ +} + +UdevEnumerator::~UdevEnumerator() +{ + if (m_enumerate) { + udev_enumerate_unref(m_enumerate); + } +} + +UdevEnumerator &UdevEnumerator::operator=(const UdevEnumerator &other) +{ + if (m_enumerate) { + udev_enumerate_unref(m_enumerate); + } + + m_context = other.m_context; + + if (other.m_enumerate) { + m_enumerate = udev_enumerate_ref(other.m_enumerate); + } else { + m_enumerate = nullptr; + } + + return *this; +} + +UdevEnumerator &UdevEnumerator::operator=(UdevEnumerator &&other) +{ + if (m_enumerate) { + udev_enumerate_unref(m_enumerate); + } + + m_context = std::move(other.m_context); + m_enumerate = std::exchange(other.m_enumerate, nullptr); + + return *this; +} + +bool UdevEnumerator::isValid() const +{ + return m_enumerate; +} + +void UdevEnumerator::matchSeat(const QString &seat) +{ + m_seat = seat; +} + +void UdevEnumerator::matchSubsystem(const QString &subsystem) +{ + if (m_enumerate) { + udev_enumerate_add_match_subsystem(m_enumerate, subsystem.toUtf8()); + } +} + +void UdevEnumerator::matchSysfsName(const QString &name) +{ + if (m_enumerate) { + udev_enumerate_add_match_sysname(m_enumerate, name.toUtf8()); + } +} + +UdevDeviceList UdevEnumerator::scan() const +{ + if (!m_enumerate) { + return UdevDeviceList(); + } + + if (udev_enumerate_scan_devices(m_enumerate)) { + return UdevDeviceList(); + } + + UdevDeviceList devices; + + for (auto it = udev_enumerate_get_list_entry(m_enumerate); + it; + it = udev_list_entry_get_next(it)) { + const QString sysfsPath = QString::fromUtf8(udev_list_entry_get_name(it)); + const UdevDevice device = m_context.deviceFromSysfsPath(sysfsPath); + if (!device.isValid()) { + continue; + } + if (device.seat() != m_seat) { + continue; + } + + devices.append(device); + } + + return devices; +} + +} // namespace KWin diff --git a/toolkit/udev_monitor.h b/toolkit/udev_monitor.h new file mode 100644 --- /dev/null +++ b/toolkit/udev_monitor.h @@ -0,0 +1,85 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2019 Vlad Zagorodniy + +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 . +*********************************************************************/ + +#pragma once + +#include "kwin_export.h" + +#include "udev_context.h" + +#include + +namespace KWin +{ + +/** + * @todo Write documentation. + **/ +class KWIN_EXPORT UdevMonitor : public QObject +{ + Q_OBJECT + +public: + explicit UdevMonitor(UdevContext *context, QObject *parent = nullptr); + ~UdevMonitor() override; + + /** + * @todo Write documentation. + **/ + bool isValid() const; + + /** + * @todo Write documentation. + **/ + void enable(); + + /** + * @todo Write documentation. + **/ + void filterBySubsystem(const QString &subsystem); + +Q_SIGNALS: + /** + * @todo Write documentation. + **/ + void deviceAdded(const UdevDevice &device); + + /** + * @todo Write documentation. + **/ + void deviceRemoved(const UdevDevice &device); + + /** + * @todo Write documentation. + **/ + void deviceChanged(const UdevDevice &device); + +private Q_SLOTS: + void dispatchEvents(); + +private: + UdevContext m_context; + udev_monitor *m_monitor; + bool m_isEnabled = false; + + Q_DISABLE_COPY(UdevMonitor) +}; + +} // namespace KWin diff --git a/toolkit/udev_monitor.cpp b/toolkit/udev_monitor.cpp new file mode 100644 --- /dev/null +++ b/toolkit/udev_monitor.cpp @@ -0,0 +1,97 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2019 Vlad Zagorodniy + +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 . +*********************************************************************/ + +#include "udev_monitor.h" +#include "udev_device.h" + +#include + +namespace KWin +{ + +UdevMonitor::UdevMonitor(UdevContext *context, QObject *parent) + : QObject(parent) + , m_context(*context) + , m_monitor(udev_monitor_new_from_netlink(*context, "udev")) +{ +} + +UdevMonitor::~UdevMonitor() +{ + if (m_monitor) { + udev_monitor_unref(m_monitor); + } +} + +bool UdevMonitor::isValid() const +{ + return m_monitor; +} + +void UdevMonitor::enable() +{ + if (m_isEnabled) { + return; + } + + udev_monitor_enable_receiving(m_monitor); + + const int fd = udev_monitor_get_fd(m_monitor); + QSocketNotifier* notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this); + connect(notifier, &QSocketNotifier::activated, this, &UdevMonitor::dispatchEvents); + + m_isEnabled = true; +} + +void UdevMonitor::filterBySubsystem(const QString &subsystem) +{ + if (!m_monitor) { + return; + } + + udev_monitor_filter_add_match_subsystem_devtype(m_monitor, subsystem.toUtf8(), nullptr); + + if (m_isEnabled) { + udev_monitor_filter_update(m_monitor); + } +} + +void UdevMonitor::dispatchEvents() +{ + UdevDevice device(udev_monitor_receive_device(m_monitor)); + if (!device.isValid()) { + return; + } + + const char *action = udev_device_get_action(device); + if (!action) { + return; + } + + if (!qstrcmp(action, "add")) { + emit deviceAdded(device); + } else if (!qstrcmp(action, "remove")) { + emit deviceRemoved(device); + } else if (!qstrcmp(action, "change")) { + emit deviceChanged(device); + } +} + +} // namespace Carbon diff --git a/udev.h b/udev.h deleted file mode 100644 --- a/udev.h +++ /dev/null @@ -1,100 +0,0 @@ -/******************************************************************** - KWin - the KDE window manager - This file is part of the KDE project. - -Copyright (C) 2014 Martin Gräßlin - -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 . -*********************************************************************/ -#ifndef KWIN_UDEV_H -#define KWIN_UDEV_H -#include -#include - -struct udev; -struct udev_device; -struct udev_monitor; - -namespace KWin -{ -class Udev; - -class KWIN_EXPORT UdevDevice -{ -public: - UdevDevice(udev_device *device); - ~UdevDevice(); - - udev_device *getParentWithSubsystemDevType(const char *subsystem, const char *devtype = nullptr) const; - const char *devNode(); - int sysNum() const; - const char *property(const char *key); - bool hasProperty(const char *key, const char *value); - - operator udev_device*() const { - return m_device; - } - operator udev_device*() { - return m_device; - } - typedef std::unique_ptr Ptr; - -private: - udev_device *m_device; -}; - -class KWIN_EXPORT UdevMonitor -{ -public: - explicit UdevMonitor(Udev *udev); - ~UdevMonitor(); - - int fd() const; - bool isValid() const { - return m_monitor != nullptr; - } - void filterSubsystemDevType(const char *subSystem, const char *devType = nullptr); - void enable(); - UdevDevice::Ptr getDevice(); - -private: - udev_monitor *m_monitor; -}; - -class KWIN_EXPORT Udev -{ -public: - Udev(); - ~Udev(); - - bool isValid() const { - return m_udev != nullptr; - } - UdevDevice::Ptr primaryGpu(); - UdevDevice::Ptr primaryFramebuffer(); - UdevDevice::Ptr deviceFromSyspath(const char *syspath); - UdevMonitor *monitor(); - operator udev*() const { - return m_udev; - } - operator udev*() { - return m_udev; - } -private: - struct udev *m_udev; -}; - -} - -#endif diff --git a/udev.cpp b/udev.cpp deleted file mode 100644 --- a/udev.cpp +++ /dev/null @@ -1,293 +0,0 @@ -/******************************************************************** - KWin - the KDE window manager - This file is part of the KDE project. - -Copyright (C) 2014 Martin Gräßlin - -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 . -*********************************************************************/ -#include "udev.h" -#include "logind.h" -// Qt -#include -#include -// system -#include -#include - -namespace KWin -{ - -Udev::Udev() - : m_udev(udev_new()) -{ -} - -Udev::~Udev() -{ - if (m_udev) { - udev_unref(m_udev); - } -} - -class UdevEnumerate -{ -public: - UdevEnumerate(Udev *udev); - ~UdevEnumerate(); - - enum class Match { - SubSystem, - SysName - }; - void addMatch(Match match, const char *name); - void scan(); - UdevDevice::Ptr find(std::function test); - -private: - Udev *m_udev; - - struct EnumerateDeleter - { - static inline void cleanup(udev_enumerate *e) - { - udev_enumerate_unref(e); - } - }; - QScopedPointer m_enumerate; -}; - -UdevEnumerate::UdevEnumerate(Udev *udev) - : m_udev(udev) - , m_enumerate(udev_enumerate_new(*m_udev)) -{ -} - -UdevEnumerate::~UdevEnumerate() = default; - -void UdevEnumerate::addMatch(UdevEnumerate::Match match, const char *name) -{ - if (m_enumerate.isNull()) { - return; - } - switch (match) { - case Match::SubSystem: - udev_enumerate_add_match_subsystem(m_enumerate.data(), name); - break; - case Match::SysName: - udev_enumerate_add_match_sysname(m_enumerate.data(), name); - break; - default: - Q_UNREACHABLE(); - break; - } -} - -void UdevEnumerate::scan() -{ - if (m_enumerate.isNull()) { - return; - } - udev_enumerate_scan_devices(m_enumerate.data()); -} - -UdevDevice::Ptr UdevEnumerate::find(std::function test) -{ - if (m_enumerate.isNull()) { - return UdevDevice::Ptr(); - } - QString defaultSeat = QStringLiteral("seat0"); - udev_list_entry *it = udev_enumerate_get_list_entry(m_enumerate.data()); - UdevDevice::Ptr firstFound; - while (it) { - auto current = it; - it = udev_list_entry_get_next(it); - auto device = m_udev->deviceFromSyspath(udev_list_entry_get_name(current)); - if (!device) { - continue; - } - QString deviceSeat = device->property("ID_SEAT"); - if (deviceSeat.isEmpty()) { - deviceSeat = defaultSeat; - } - if (deviceSeat != LogindIntegration::self()->seat()) { - continue; - } - if (test(device)) { - return device; - } - if (!firstFound) { - firstFound.swap(device); - } - } - return firstFound; -} - -UdevDevice::Ptr Udev::primaryGpu() -{ - if (!m_udev) { - return UdevDevice::Ptr(); - } - UdevEnumerate enumerate(this); - enumerate.addMatch(UdevEnumerate::Match::SubSystem, "drm"); - enumerate.addMatch(UdevEnumerate::Match::SysName, "card[0-9]*"); - enumerate.scan(); - return enumerate.find([](const UdevDevice::Ptr &device) { - auto pci = device->getParentWithSubsystemDevType("pci"); - if (!pci) { - return false; - } - const char *systAttrValue = udev_device_get_sysattr_value(pci, "boot_vga"); - if (systAttrValue && qstrcmp(systAttrValue, "1") == 0) { - return true; - } - return false; - }); -} - -UdevDevice::Ptr Udev::primaryFramebuffer() -{ - if (!m_udev) { - return UdevDevice::Ptr(); - } - UdevEnumerate enumerate(this); - enumerate.addMatch(UdevEnumerate::Match::SubSystem, "graphics"); - enumerate.addMatch(UdevEnumerate::Match::SysName, "fb[0-9]*"); - enumerate.scan(); - return enumerate.find([](const UdevDevice::Ptr &device) { - auto pci = device->getParentWithSubsystemDevType("pci"); - if (!pci) { - return false; - } - const char *systAttrValue = udev_device_get_sysattr_value(pci, "boot_vga"); - if (systAttrValue && qstrcmp(systAttrValue, "1") == 0) { - return true; - } - return false; - }); -} - -UdevDevice::Ptr Udev::deviceFromSyspath(const char *syspath) -{ - return UdevDevice::Ptr(new UdevDevice(udev_device_new_from_syspath(m_udev, syspath))); -} - -UdevMonitor *Udev::monitor() -{ - UdevMonitor *m = new UdevMonitor(this); - if (!m->isValid()) { - delete m; - m = nullptr; - } - return m; -} - -UdevDevice::UdevDevice(udev_device *device) - : m_device(device) -{ -} - -UdevDevice::~UdevDevice() -{ - if (m_device) { - udev_device_unref(m_device); - } -} - -udev_device *UdevDevice::getParentWithSubsystemDevType(const char *subsystem, const char *devtype) const -{ - if (!m_device) { - return nullptr; - } - return udev_device_get_parent_with_subsystem_devtype(m_device, subsystem, devtype); -} - -const char *UdevDevice::devNode() -{ - if (!m_device) { - return nullptr; - } - return udev_device_get_devnode(m_device); -} - -int UdevDevice::sysNum() const -{ - if (!m_device) { - return 0; - } - return QByteArray(udev_device_get_sysnum(m_device)).toInt(); -} - -const char *UdevDevice::property(const char *key) -{ - if (!m_device) { - return nullptr; - } - return udev_device_get_property_value(m_device, key); -} - -bool UdevDevice::hasProperty(const char *key, const char *value) -{ - const char *p = property(key); - if (!p) { - return false; - } - return qstrcmp(p, value) == 0; -} - -UdevMonitor::UdevMonitor(Udev *udev) - : m_monitor(udev_monitor_new_from_netlink(*udev, "udev")) -{ -} - -UdevMonitor::~UdevMonitor() -{ - if (m_monitor) { - udev_monitor_unref(m_monitor); - } -} - -int UdevMonitor::fd() const -{ - if (m_monitor) { - return udev_monitor_get_fd(m_monitor); - } - return -1; -} - -void UdevMonitor::filterSubsystemDevType(const char *subSystem, const char *devType) -{ - if (!m_monitor) { - return; - } - udev_monitor_filter_add_match_subsystem_devtype(m_monitor, subSystem, devType); -} - -void UdevMonitor::enable() -{ - if (!m_monitor) { - return; - } - udev_monitor_enable_receiving(m_monitor); -} - -UdevDevice::Ptr UdevMonitor::getDevice() -{ - if (!m_monitor) { - return UdevDevice::Ptr(); - } - return UdevDevice::Ptr(new UdevDevice(udev_monitor_receive_device(m_monitor))); -} - -}