diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index a7975f7ea..efe1fd9a2 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -1,438 +1,441 @@ add_definitions(-DKWIN_UNIT_TEST) remove_definitions(-DQT_USE_QSTRINGBUILDER) add_subdirectory(libkwineffects) add_subdirectory(libxrenderutils) add_subdirectory(integration) if (HAVE_INPUT) add_subdirectory(libinput) endif() +if (HAVE_DRM) + add_subdirectory(drm) +endif() add_subdirectory(tabbox) ######################################################## # Test ScreenPaintData ######################################################## set( testScreenPaintData_SRCS test_screen_paint_data.cpp ) add_executable(testScreenPaintData ${testScreenPaintData_SRCS}) target_link_libraries( testScreenPaintData kwineffects Qt5::Test Qt5::Widgets KF5::WindowSystem) add_test(kwin-testScreenPaintData testScreenPaintData) ecm_mark_as_test(testScreenPaintData) ######################################################## # Test WindowPaintData ######################################################## set( testWindowPaintData_SRCS test_window_paint_data.cpp ) add_executable(testWindowPaintData ${testWindowPaintData_SRCS}) target_link_libraries( testWindowPaintData kwineffects Qt5::Widgets Qt5::Test ) add_test(kwin-testWindowPaintData testWindowPaintData) ecm_mark_as_test(testWindowPaintData) ######################################################## # Test VirtualDesktopManager ######################################################## set( testVirtualDesktops_SRCS test_virtual_desktops.cpp ../virtualdesktops.cpp ) add_executable(testVirtualDesktops ${testVirtualDesktops_SRCS}) target_link_libraries( testVirtualDesktops Qt5::Test Qt5::Widgets KF5::I18n KF5::GlobalAccel KF5::ConfigCore KF5::WindowSystem ) add_test(kwin-testVirtualDesktops testVirtualDesktops) ecm_mark_as_test(testVirtualDesktops) ######################################################## # Test ClientMachine ######################################################## set( testClientMachine_SRCS test_client_machine.cpp ../client_machine.cpp ) add_executable( testClientMachine ${testClientMachine_SRCS} ) set_target_properties(testClientMachine PROPERTIES COMPILE_DEFINITIONS "NO_NONE_WINDOW") target_link_libraries( testClientMachine Qt5::Concurrent Qt5::Test Qt5::X11Extras Qt5::Widgets KF5::ConfigCore KF5::WindowSystem XCB::XCB XCB::XFIXES ${X11_X11_LIB} # to make jenkins happy ) add_test(kwin-testClientMachine testClientMachine) ecm_mark_as_test(testClientMachine) ######################################################## # Test XcbWrapper ######################################################## set( testXcbWrapper_SRCS test_xcb_wrapper.cpp ) add_executable( testXcbWrapper ${testXcbWrapper_SRCS} ) target_link_libraries( testXcbWrapper Qt5::Test Qt5::X11Extras Qt5::Widgets KF5::ConfigCore KF5::WindowSystem XCB::XCB ) add_test(kwin-testXcbWrapper testXcbWrapper) ecm_mark_as_test(testXcbWrapper) if (XCB_ICCCM_FOUND) add_executable( testXcbSizeHints test_xcb_size_hints.cpp ) set_target_properties(testXcbSizeHints PROPERTIES COMPILE_DEFINITIONS "NO_NONE_WINDOW") target_link_libraries( testXcbSizeHints Qt5::Test Qt5::X11Extras Qt5::Widgets KF5::ConfigCore KF5::WindowSystem XCB::XCB XCB::ICCCM ) add_test(kwin-testXcbSizeHints testXcbSizeHints) ecm_mark_as_test(testXcbSizeHints) endif() ######################################################## # Test XcbWindow ######################################################## set( testXcbWindow_SRCS test_xcb_window.cpp ) add_executable( testXcbWindow ${testXcbWindow_SRCS} ) target_link_libraries( testXcbWindow Qt5::Test Qt5::X11Extras Qt5::Widgets KF5::ConfigCore KF5::WindowSystem XCB::XCB ) add_test(kwin-testXcbWindow testXcbWindow) ecm_mark_as_test(testXcbWindow) ######################################################## # Test BuiltInEffectLoader ######################################################## set( testBuiltInEffectLoader_SRCS test_builtin_effectloader.cpp mock_effectshandler.cpp ../effectloader.cpp ) add_executable( testBuiltInEffectLoader ${testBuiltInEffectLoader_SRCS}) set_target_properties(testBuiltInEffectLoader PROPERTIES COMPILE_DEFINITIONS "NO_NONE_WINDOW") target_link_libraries(testBuiltInEffectLoader Qt5::Concurrent Qt5::Test Qt5::X11Extras KF5::Package kwineffects kwin4_effect_builtins ) add_test(kwin-testBuiltInEffectLoader testBuiltInEffectLoader) ecm_mark_as_test(testBuiltInEffectLoader) ######################################################## # Test ScriptedEffectLoader ######################################################## include_directories(${KWIN_SOURCE_DIR}) set( testScriptedEffectLoader_SRCS test_scripted_effectloader.cpp mock_abstract_client.cpp mock_effectshandler.cpp mock_screens.cpp mock_workspace.cpp ../effectloader.cpp ../scripting/scriptedeffect.cpp ../scripting/scriptingutils.cpp ../scripting/scripting_logging.cpp ../screens.cpp ../orientation_sensor.cpp ) kconfig_add_kcfg_files(testScriptedEffectLoader_SRCS ../settings.kcfgc) add_executable( testScriptedEffectLoader ${testScriptedEffectLoader_SRCS}) target_link_libraries(testScriptedEffectLoader Qt5::Concurrent Qt5::Qml Qt5::Script Qt5::Sensors Qt5::Test Qt5::X11Extras KF5::ConfigGui KF5::GlobalAccel KF5::I18n KF5::Notifications KF5::Package kwineffects kwin4_effect_builtins ) add_test(kwin-testScriptedEffectLoader testScriptedEffectLoader) ecm_mark_as_test(testScriptedEffectLoader) ######################################################## # Test PluginEffectLoader ######################################################## set( testPluginEffectLoader_SRCS test_plugin_effectloader.cpp mock_effectshandler.cpp ../effectloader.cpp ) add_executable( testPluginEffectLoader ${testPluginEffectLoader_SRCS}) target_link_libraries(testPluginEffectLoader Qt5::Concurrent Qt5::Test Qt5::X11Extras KF5::Package kwineffects kwin4_effect_builtins ) add_test(kwin-testPluginEffectLoader testPluginEffectLoader) ecm_mark_as_test(testPluginEffectLoader) ######################################################## # FakeEffectPlugin ######################################################## add_library(fakeeffectplugin MODULE fakeeffectplugin.cpp) set_target_properties(fakeeffectplugin PROPERTIES PREFIX "") target_link_libraries(fakeeffectplugin kwineffects) ######################################################## # FakeEffectPlugin-Version ######################################################## add_library(effectversionplugin MODULE fakeeffectplugin_version.cpp) set_target_properties(effectversionplugin PROPERTIES PREFIX "") target_link_libraries(effectversionplugin kwineffects) ######################################################## # Test Screens ######################################################## set( testScreens_SRCS test_screens.cpp mock_abstract_client.cpp mock_client.cpp mock_screens.cpp mock_workspace.cpp ../screens.cpp ../x11eventfilter.cpp ../orientation_sensor.cpp ) kconfig_add_kcfg_files(testScreens_SRCS ../settings.kcfgc) add_executable( testScreens ${testScreens_SRCS}) target_include_directories(testScreens BEFORE PRIVATE ./) target_link_libraries(testScreens Qt5::Sensors Qt5::Test Qt5::X11Extras Qt5::Widgets KF5::ConfigCore KF5::ConfigGui KF5::I18n KF5::Notifications KF5::WindowSystem ) add_test(kwin_testScreens testScreens) ecm_mark_as_test(testScreens) ######################################################## # Test XrandRScreens ######################################################## set( testXRandRScreens_SRCS test_xrandr_screens.cpp mock_abstract_client.cpp mock_client.cpp mock_screens.cpp mock_workspace.cpp ../screens.cpp ../plugins/platforms/x11/standalone/screens_xrandr.cpp ../xcbutils.cpp # init of extensions ../x11eventfilter.cpp ../orientation_sensor.cpp ) kconfig_add_kcfg_files(testXRandRScreens_SRCS ../settings.kcfgc) add_executable( testXRandRScreens ${testXRandRScreens_SRCS} ) target_link_libraries( testXRandRScreens Qt5::Test Qt5::Sensors Qt5::Widgets KF5::ConfigCore KF5::ConfigGui KF5::I18n KF5::Notifications KF5::WindowSystem XCB::XCB XCB::RANDR XCB::XFIXES XCB::SYNC XCB::COMPOSITE XCB::DAMAGE XCB::GLX XCB::SHM ) add_test(kwin-testXRandRScreens testXRandRScreens) ecm_mark_as_test(testXRandRScreens) ######################################################## # Test ScreenEdges ######################################################## set( testScreenEdges_SRCS test_screen_edges.cpp mock_abstract_client.cpp mock_client.cpp mock_screens.cpp mock_workspace.cpp ../atoms.cpp ../gestures.cpp ../screens.cpp ../screenedge.cpp ../virtualdesktops.cpp ../xcbutils.cpp # init of extensions ../plugins/platforms/x11/standalone/edge.cpp ../orientation_sensor.cpp ) kconfig_add_kcfg_files(testScreenEdges_SRCS ../settings.kcfgc) qt5_add_dbus_interface( testScreenEdges_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../org.freedesktop.ScreenSaver.xml screenlocker_interface) add_executable( testScreenEdges ${testScreenEdges_SRCS}) set_target_properties(testScreenEdges PROPERTIES COMPILE_DEFINITIONS "NO_NONE_WINDOW") target_include_directories(testScreenEdges BEFORE PRIVATE ./) target_link_libraries(testScreenEdges Qt5::DBus Qt5::Sensors Qt5::Test Qt5::X11Extras KF5::ConfigCore KF5::ConfigGui KF5::I18n KF5::GlobalAccel KF5::Notifications KF5::WindowSystem XCB::XCB XCB::RANDR XCB::XFIXES XCB::SYNC XCB::COMPOSITE XCB::DAMAGE XCB::GLX XCB::SHM ) add_test(kwin_testScreenEdges testScreenEdges) ecm_mark_as_test(testScreenEdges) ######################################################## # Test OnScreenNotification ######################################################## set( testOnScreenNotification_SRCS onscreennotificationtest.cpp ../onscreennotification.cpp ../input_event_spy.cpp ) add_executable( testOnScreenNotification ${testOnScreenNotification_SRCS}) target_link_libraries(testOnScreenNotification Qt5::Test Qt5::Widgets # QAction include Qt5::Quick KF5::ConfigCore ) add_test(kwin-testOnScreenNotification testOnScreenNotification) ecm_mark_as_test(testOnScreenNotification) ######################################################## # Test Gestures ######################################################## set( testGestures_SRCS test_gestures.cpp ../gestures.cpp ) add_executable( testGestures ${testGestures_SRCS}) target_link_libraries(testGestures Qt5::Test ) add_test(kwin-testGestures testGestures) ecm_mark_as_test(testGestures) ######################################################## # Test X11 TimestampUpdate ######################################################## add_executable(testX11TimestampUpdate test_x11_timestamp_update.cpp) target_compile_definitions(testX11TimestampUpdate PRIVATE KWINBACKENDPATH="${CMAKE_BINARY_DIR}/plugins/platforms/x11/standalone/KWinX11Platform.so") target_link_libraries(testX11TimestampUpdate Qt5::Test KF5::CoreAddons kwin ) add_test(kwin-testX11TimestampUpdate testX11TimestampUpdate) ecm_mark_as_test(testX11TimestampUpdate) set(testOpenGLContextAttributeBuilder_SRCS opengl_context_attribute_builder_test.cpp ../abstract_opengl_context_attribute_builder.cpp ../egl_context_attribute_builder.cpp ) if(HAVE_EPOXY_GLX) set(testOpenGLContextAttributeBuilder_SRCS ${testOpenGLContextAttributeBuilder_SRCS} ../plugins/platforms/x11/standalone/glx_context_attribute_builder.cpp) endif() add_executable(testOpenGLContextAttributeBuilder ${testOpenGLContextAttributeBuilder_SRCS}) target_link_libraries(testOpenGLContextAttributeBuilder Qt5::Test) add_test(kwin-testOpenGLContextAttributeBuilder testOpenGLContextAttributeBuilder) ecm_mark_as_test(testOpenGLContextAttributeBuilder) set(testXkb_SRCS test_xkb.cpp ../xkb.cpp ) add_executable(testXkb ${testXkb_SRCS}) target_link_libraries(testXkb Qt5::Test Qt5::Gui Qt5::Widgets KF5::ConfigCore KF5::WindowSystem KF5::WaylandServer XKB::XKB ) add_test(kwin-testXkb testXkb) ecm_mark_as_test(testXkb) if(HAVE_GBM) add_executable(testGbmSurface test_gbm_surface.cpp ../plugins/platforms/drm/gbm_surface.cpp) target_link_libraries(testGbmSurface Qt5::Test) add_test(kwin-testGbmSurface testGbmSurface) ecm_mark_as_test(testGbmSurface) endif() add_executable(testVirtualKeyboardDBus test_virtualkeyboard_dbus.cpp ../virtualkeyboard_dbus.cpp) target_link_libraries(testVirtualKeyboardDBus Qt5::Test Qt5::DBus ) add_test(kwin-testVirtualKeyboardDBus testVirtualKeyboardDBus) ecm_mark_as_test(testVirtualKeyboardDBus) diff --git a/autotests/drm/CMakeLists.txt b/autotests/drm/CMakeLists.txt new file mode 100644 index 000000000..453ab5abf --- /dev/null +++ b/autotests/drm/CMakeLists.txt @@ -0,0 +1,26 @@ +include_directories(${Libdrm_INCLUDE_DIRS}) + +set(mockDRM_SRCS + mock_drm.cpp + ../../plugins/platforms/drm/drm_buffer.cpp + ../../plugins/platforms/drm/drm_object.cpp + ../../plugins/platforms/drm/drm_object_connector.cpp + ../../plugins/platforms/drm/drm_object_plane.cpp + ../../plugins/platforms/drm/logging.cpp +) + +add_library(mockDrm STATIC ${mockDRM_SRCS}) +target_link_libraries(mockDrm Qt5::Gui) +ecm_mark_as_test(mockDrm) + +function(drmTest) + set(oneValueArgs NAME) + set(multiValueArgs SRCS ) + cmake_parse_arguments(ARGS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + add_executable(${ARGS_NAME} ${ARGS_SRCS}) + target_link_libraries(${ARGS_NAME} mockDrm Qt5::Test) + add_test(kwin-drm-${ARGS_NAME} ${ARGS_NAME}) + ecm_mark_as_test(${ARGS_NAME}) +endfunction() + +drmTest(NAME objecttest SRCS objecttest.cpp) diff --git a/autotests/drm/mock_drm.cpp b/autotests/drm/mock_drm.cpp new file mode 100644 index 000000000..70123e266 --- /dev/null +++ b/autotests/drm/mock_drm.cpp @@ -0,0 +1,78 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2017 Martin Flöser + +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 "mock_drm.h" + +#include +#include + +static QMap> s_drmProperties{}; + +namespace MockDrm +{ + +void addDrmModeProperties(int fd, const QVector<_drmModeProperty> &properties) +{ + s_drmProperties.insert(fd, properties); +} + +} + +int drmModeAtomicAddProperty(drmModeAtomicReqPtr req, uint32_t object_id, uint32_t property_id, uint64_t value) +{ + Q_UNUSED(req) + Q_UNUSED(object_id) + Q_UNUSED(property_id) + Q_UNUSED(value) + return 0; +} + +drmModePropertyPtr drmModeGetProperty(int fd, uint32_t propertyId) +{ + auto it = s_drmProperties.find(fd); + if (it == s_drmProperties.end()) { + return nullptr; + } + auto it2 = std::find_if(it->constBegin(), it->constEnd(), + [propertyId] (const auto &property) { + return property.prop_id == propertyId; + } + ); + if (it2 == it->constEnd()) { + return nullptr; + } + + auto *property = new _drmModeProperty; + property->prop_id = it2->prop_id; + property->flags = it2->flags; + strcpy(property->name, it2->name); + property->count_values = it2->count_values; + property->values = it2->values; + property->count_enums = it2->count_enums; + property->enums = it2->enums; + property->count_blobs = it2->count_blobs; + property->blob_ids = it2->blob_ids; + + return property; +} + +void drmModeFreeProperty(drmModePropertyPtr ptr) +{ + delete ptr; +} diff --git a/plugins/platforms/drm/drm_object_connector.h b/autotests/drm/mock_drm.h similarity index 59% copy from plugins/platforms/drm/drm_object_connector.h copy to autotests/drm/mock_drm.h index 0f6fcdbd4..ae8076977 100644 --- a/plugins/platforms/drm/drm_object_connector.h +++ b/autotests/drm/mock_drm.h @@ -1,57 +1,32 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. -Copyright (C) 2016 Roman Gilg +Copyright (C) 2017 Martin Flöser 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_DRM_OBJECT_CONNECTOR_H -#define KWIN_DRM_OBJECT_CONNECTOR_H +#pragma once +#include +#include +#include -#include "drm_object.h" +#include -namespace KWin +namespace MockDrm { -class DrmConnector : public DrmObject -{ -public: - DrmConnector(uint32_t connector_id, DrmBackend *backend); - - virtual ~DrmConnector(); - - bool atomicInit(); - - enum class PropertyIndex { - CrtcId = 0, - Count - }; - - QVector encoders() { - return m_encoders; - } - - bool initProps(); - bool isConnected(); - - -private: - QVector m_encoders; -}; +void addDrmModeProperties(int fd, const QVector<_drmModeProperty> &properties); } - -#endif - diff --git a/autotests/drm/objecttest.cpp b/autotests/drm/objecttest.cpp new file mode 100644 index 000000000..384268b6c --- /dev/null +++ b/autotests/drm/objecttest.cpp @@ -0,0 +1,218 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2017 Martin Flöser + +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 "mock_drm.h" +#include "../../plugins/platforms/drm/drm_object.h" +#include + +class MockDrmObject : public KWin::DrmObject +{ +public: + MockDrmObject(uint32_t id, int fd) + : DrmObject(id, fd) + { + } + ~MockDrmObject() override {} + bool atomicInit() override; + bool initProps() override; + + void setProperties(uint32_t count, uint32_t *props, uint64_t *values) { + m_count = count; + m_props = props; + m_values = values; + } + + QByteArray name(int prop) const { + auto property = DrmObject::m_props.at(prop); + if (!property) { + return QByteArray(); + } + return property->name(); + } + + uint32_t propertyId(int prop) const { + auto property = DrmObject::m_props.at(prop); + if (!property) { + return 0xFFFFFFFFu; + } + return property->propId(); + } + +private: + uint32_t m_count = 0; + uint32_t *m_props = nullptr; + uint64_t *m_values = nullptr; +}; + +bool MockDrmObject::atomicInit() +{ + return initProps(); +} + +bool MockDrmObject::initProps() +{ + setPropertyNames({"foo", "bar", "baz"}); + drmModeObjectProperties properties{m_count, m_props, m_values}; + for (int i = 0; i < 3; i++) { + initProp(i, &properties); + } + return false; +} + +class ObjectTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void testId_data(); + void testId(); + void testFd_data(); + void testFd(); + void testOutput(); + void testInitProperties(); +}; + +void ObjectTest::testId_data() +{ + QTest::addColumn("id"); + + QTest::newRow("0") << 0u; + QTest::newRow("1") << 1u; + QTest::newRow("10") << 10u; + QTest::newRow("uint max") << 0xFFFFFFFFu; +} + +void ObjectTest::testId() +{ + QFETCH(quint32, id); + MockDrmObject object{id, -1}; + QCOMPARE(object.id(), id); +} + +void ObjectTest::testFd_data() +{ + QTest::addColumn("fd"); + + QTest::newRow("-1") << -1; + QTest::newRow("0") << 0; + QTest::newRow("1") << 1; + QTest::newRow("2") << 2; + QTest::newRow("100") << 100; + QTest::newRow("int max") << 0x7FFFFFFF; +} + +void ObjectTest::testFd() +{ + QFETCH(int, fd); + MockDrmObject object{0, fd}; + QCOMPARE(object.fd(), fd); +} + +namespace KWin +{ +struct DrmOutput { + int foo; +}; +} + +void ObjectTest::testOutput() +{ + MockDrmObject object{0, 1}; + + QVERIFY(!object.output()); + + KWin::DrmOutput output{2}; + object.setOutput(&output); + QCOMPARE(object.output(), &output); + QCOMPARE(object.output()->foo, 2); +} + +void ObjectTest::testInitProperties() +{ + MockDrmObject object{0, 20}; + uint32_t propertiesIds[] = { 0, 1, 2, 3}; + uint64_t values[] = { 0, 2, 10, 20 }; + object.setProperties(4, propertiesIds, values); + + MockDrm::addDrmModeProperties(20, QVector<_drmModeProperty>{ + _drmModeProperty{ + 0, + 0, + "foo\0", + 0, + nullptr, + 0, + nullptr, + 0, + nullptr + }, + _drmModeProperty{ + 1, + 0, + "foobar\0", + 0, + nullptr, + 0, + nullptr, + 0, + nullptr + }, + _drmModeProperty{ + 2, + 0, + "baz\0", + 0, + nullptr, + 0, + nullptr, + 0, + nullptr + }, + _drmModeProperty{ + 3, + 0, + "foobarbaz\0", + 0, + nullptr, + 0, + nullptr, + 0, + nullptr + } + }); + + object.atomicInit(); + + // verify the names + QCOMPARE(object.name(0), QByteArrayLiteral("foo")); + QCOMPARE(object.name(1), QByteArray()); + QCOMPARE(object.name(2), QByteArrayLiteral("baz")); + + // verify the property ids + QCOMPARE(object.propertyId(0), 0u); + QCOMPARE(object.propertyId(1), 0xFFFFFFFFu); + QCOMPARE(object.propertyId(2), 2u); + + // doesn't have enums + QCOMPARE(object.propHasEnum(0, 0), false); + QCOMPARE(object.propHasEnum(1, 0), false); + QCOMPARE(object.propHasEnum(2, 0), false); +} + +QTEST_GUILESS_MAIN(ObjectTest) +#include "objecttest.moc" diff --git a/cmake/modules/FindLibdrm.cmake b/cmake/modules/FindLibdrm.cmake index ebaa87dc9..9936e07ee 100644 --- a/cmake/modules/FindLibdrm.cmake +++ b/cmake/modules/FindLibdrm.cmake @@ -1,126 +1,126 @@ #.rst: # FindLibdrm # ------- # # Try to find libdrm on a Unix system. # # This will define the following variables: # # ``Libdrm_FOUND`` # True if (the requested version of) libdrm is available # ``Libdrm_VERSION`` # The version of libdrm # ``Libdrm_LIBRARIES`` # This can be passed to target_link_libraries() instead of the ``Libdrm::Libdrm`` # target # ``Libdrm_INCLUDE_DIRS`` # This should be passed to target_include_directories() if the target is not # used for linking # ``Libdrm_DEFINITIONS`` # This should be passed to target_compile_options() if the target is not # used for linking # # If ``Libdrm_FOUND`` is TRUE, it will also define the following imported target: # # ``Libdrm::Libdrm`` # The libdrm library # # In general we recommend using the imported target, as it is easier to use. # Bear in mind, however, that if the target is in the link interface of an # exported library, it must be made available by the package config file. #============================================================================= # Copyright 2014 Alex Merry # Copyright 2014 Martin Gräßlin # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #============================================================================= if(CMAKE_VERSION VERSION_LESS 2.8.12) message(FATAL_ERROR "CMake 2.8.12 is required by FindLibdrm.cmake") endif() if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12) message(AUTHOR_WARNING "Your project should require at least CMake 2.8.12 to use FindLibdrm.cmake") endif() if(NOT WIN32) # Use pkg-config to get the directories and then use these values # in the FIND_PATH() and FIND_LIBRARY() calls find_package(PkgConfig) pkg_check_modules(PKG_Libdrm QUIET libdrm) set(Libdrm_DEFINITIONS ${PKG_Libdrm_CFLAGS_OTHER}) set(Libdrm_VERSION ${PKG_Libdrm_VERSION}) find_path(Libdrm_INCLUDE_DIR NAMES xf86drm.h HINTS ${PKG_Libdrm_INCLUDE_DIRS} ) find_library(Libdrm_LIBRARY NAMES drm HINTS ${PKG_Libdrm_LIBRARY_DIRS} ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Libdrm FOUND_VAR Libdrm_FOUND REQUIRED_VARS Libdrm_LIBRARY Libdrm_INCLUDE_DIR VERSION_VAR Libdrm_VERSION ) if(Libdrm_FOUND AND NOT TARGET Libdrm::Libdrm) add_library(Libdrm::Libdrm UNKNOWN IMPORTED) set_target_properties(Libdrm::Libdrm PROPERTIES IMPORTED_LOCATION "${Libdrm_LIBRARY}" INTERFACE_COMPILE_OPTIONS "${Libdrm_DEFINITIONS}" INTERFACE_INCLUDE_DIRECTORIES "${Libdrm_INCLUDE_DIR}" INTERFACE_INCLUDE_DIRECTORIES "${Libdrm_INCLUDE_DIR}/libdrm" ) endif() mark_as_advanced(Libdrm_LIBRARY Libdrm_INCLUDE_DIR) # compatibility variables set(Libdrm_LIBRARIES ${Libdrm_LIBRARY}) - set(Libdrm_INCLUDE_DIRS ${Libdrm_INCLUDE_DIR}) + set(Libdrm_INCLUDE_DIRS ${Libdrm_INCLUDE_DIR} "${Libdrm_INCLUDE_DIR}/libdrm") set(Libdrm_VERSION_STRING ${Libdrm_VERSION}) else() message(STATUS "FindLibdrm.cmake cannot find libdrm on Windows systems.") set(Libdrm_FOUND FALSE) endif() include(FeatureSummary) set_package_properties(Libdrm PROPERTIES URL "https://wiki.freedesktop.org/dri/" DESCRIPTION "Userspace interface to kernel DRM services." ) diff --git a/plugins/platforms/drm/drm_backend.cpp b/plugins/platforms/drm/drm_backend.cpp index bb35b6dec..4d48b6a78 100644 --- a/plugins/platforms/drm/drm_backend.cpp +++ b/plugins/platforms/drm/drm_backend.cpp @@ -1,773 +1,773 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 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 "drm_backend.h" #include "drm_output.h" #include "drm_object_connector.h" #include "drm_object_crtc.h" #include "drm_object_plane.h" #include "composite.h" #include "cursor.h" #include "logging.h" #include "logind.h" #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 // KWayland #include #include // KF5 #include #include #include #include // Qt #include #include #include // system #include // drm #include #include #include #ifndef DRM_CAP_CURSOR_WIDTH #define DRM_CAP_CURSOR_WIDTH 0x8 #endif #ifndef DRM_CAP_CURSOR_HEIGHT #define DRM_CAP_CURSOR_HEIGHT 0x9 #endif #define KWIN_DRM_EVENT_CONTEXT_VERSION 2 namespace KWin { DrmBackend::DrmBackend(QObject *parent) : Platform(parent) , m_udev(new Udev) , m_udevMonitor(m_udev->monitor()) , m_dpmsFilter() { handleOutputs(); } DrmBackend::~DrmBackend() { #if HAVE_GBM if (m_gbmDevice) { gbm_device_destroy(m_gbmDevice); } #endif if (m_fd >= 0) { // wait for pageflips while (m_pageFlipsPending != 0) { QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents); } qDeleteAll(m_outputs); qDeleteAll(m_planes); qDeleteAll(m_crtcs); qDeleteAll(m_connectors); close(m_fd); } } void DrmBackend::init() { LogindIntegration *logind = LogindIntegration::self(); auto takeControl = [logind, this]() { if (logind->hasSessionControl()) { openDrm(); } else { logind->takeControl(); connect(logind, &LogindIntegration::hasSessionControlChanged, this, &DrmBackend::openDrm); } }; if (logind->isConnected()) { takeControl(); } else { connect(logind, &LogindIntegration::connectedChanged, this, takeControl); } } void DrmBackend::outputWentOff() { if (!m_dpmsFilter.isNull()) { // already another output is off return; } m_dpmsFilter.reset(new DpmsInputEventFilter(this)); input()->prependInputEventFilter(m_dpmsFilter.data()); } void DrmBackend::turnOutputsOn() { m_dpmsFilter.reset(); for (auto it = m_enabledOutputs.constBegin(), end = m_enabledOutputs.constEnd(); it != end; it++) { (*it)->setDpms(DrmOutput::DpmsMode::On); } } void DrmBackend::checkOutputsAreOn() { if (m_dpmsFilter.isNull()) { // already disabled, all outputs are on return; } for (auto it = m_enabledOutputs.constBegin(), end = m_enabledOutputs.constEnd(); it != end; it++) { if (!(*it)->isDpmsEnabled()) { // dpms still disabled, need to keep the filter return; } } // all outputs are on, disable the filter m_dpmsFilter.reset(); } void DrmBackend::activate(bool active) { if (active) { qCDebug(KWIN_DRM) << "Activating session."; reactivate(); } else { qCDebug(KWIN_DRM) << "Deactivating session."; deactivate(); } } void DrmBackend::reactivate() { if (m_active) { return; } m_active = true; if (!usesSoftwareCursor()) { const QPoint cp = Cursor::pos() - softwareCursorHotspot(); for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { DrmOutput *o = *it; // only relevant in atomic mode o->m_modesetRequested = true; o->pageFlipped(); // TODO: Do we really need this? o->m_crtc->blank(); o->showCursor(); o->moveCursor(cp); } } // restart compositor m_pageFlipsPending = 0; if (Compositor *compositor = Compositor::self()) { compositor->bufferSwapComplete(); compositor->addRepaintFull(); } } void DrmBackend::deactivate() { if (!m_active) { return; } // block compositor if (m_pageFlipsPending == 0 && Compositor::self()) { Compositor::self()->aboutToSwapBuffers(); } // hide cursor and disable for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { DrmOutput *o = *it; o->hideCursor(); } m_active = false; } void DrmBackend::pageFlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data) { Q_UNUSED(fd) Q_UNUSED(frame) Q_UNUSED(sec) Q_UNUSED(usec) auto output = reinterpret_cast(data); output->pageFlipped(); output->m_backend->m_pageFlipsPending--; if (output->m_backend->m_pageFlipsPending == 0) { // TODO: improve, this currently means we wait for all page flips or all outputs. // It would be better to driver the repaint per output if (output->m_dpmsAtomicOffPending) { output->m_modesetRequested = true; output->dpmsAtomicOff(); } if (Compositor::self()) { Compositor::self()->bufferSwapComplete(); } } } void DrmBackend::openDrm() { connect(LogindIntegration::self(), &LogindIntegration::sessionActiveChanged, this, &DrmBackend::activate); UdevDevice::Ptr device = m_udev->primaryGpu(); if (!device) { qCWarning(KWIN_DRM) << "Did not find a GPU"; return; } int fd = LogindIntegration::self()->takeDevice(device->devNode()); if (fd < 0) { qCWarning(KWIN_DRM) << "failed to open drm device at" << device->devNode(); return; } m_fd = fd; m_active = true; QSocketNotifier *notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this); connect(notifier, &QSocketNotifier::activated, this, [this] { if (!LogindIntegration::self()->isActiveSession()) { return; } drmEventContext e; memset(&e, 0, sizeof e); e.version = KWIN_DRM_EVENT_CONTEXT_VERSION; e.page_flip_handler = pageFlipHandler; drmHandleEvent(m_fd, &e); } ); m_drmId = device->sysNum(); // trying to activate Atomic Mode Setting (this means also Universal Planes) if (!qEnvironmentVariableIsSet("KWIN_DRM_NO_AMS")) { if (drmSetClientCap(m_fd, DRM_CLIENT_CAP_ATOMIC, 1) == 0) { qCDebug(KWIN_DRM) << "Using Atomic Mode Setting."; m_atomicModeSetting = true; ScopedDrmPointer planeResources(drmModeGetPlaneResources(m_fd)); if (!planeResources) { qCWarning(KWIN_DRM) << "Failed to get plane resources. Falling back to legacy mode"; m_atomicModeSetting = false; } if (m_atomicModeSetting) { qCDebug(KWIN_DRM) << "Number of planes:" << planeResources->count_planes; // create the plane objects for (unsigned int i = 0; i < planeResources->count_planes; ++i) { drmModePlane *kplane = drmModeGetPlane(m_fd, planeResources->planes[i]); - DrmPlane *p = new DrmPlane(kplane->plane_id, this); + DrmPlane *p = new DrmPlane(kplane->plane_id, m_fd); if (p->atomicInit()) { m_planes << p; if (p->type() == DrmPlane::TypeIndex::Overlay) { m_overlayPlanes << p; } } else { delete p; } } if (m_planes.isEmpty()) { qCWarning(KWIN_DRM) << "Failed to create any plane. Falling back to legacy mode"; m_atomicModeSetting = false; } } } else { qCWarning(KWIN_DRM) << "drmSetClientCap for Atomic Mode Setting failed. Using legacy mode."; } } ScopedDrmPointer<_drmModeRes, &drmModeFreeResources> resources(drmModeGetResources(m_fd)); drmModeRes *res = resources.data(); if (!resources) { qCWarning(KWIN_DRM) << "drmModeGetResources failed"; return; } for (int i = 0; i < res->count_connectors; ++i) { - m_connectors << new DrmConnector(res->connectors[i], this); + m_connectors << new DrmConnector(res->connectors[i], m_fd); } for (int i = 0; i < res->count_crtcs; ++i) { m_crtcs << new DrmCrtc(res->crtcs[i], this, i); } if (m_atomicModeSetting) { auto tryAtomicInit = [] (DrmObject *obj) -> bool { if (obj->atomicInit()) { return false; } else { delete obj; return true; } }; m_connectors.erase(std::remove_if(m_connectors.begin(), m_connectors.end(), tryAtomicInit), m_connectors.end()); m_crtcs.erase(std::remove_if(m_crtcs.begin(), m_crtcs.end(), tryAtomicInit), m_crtcs.end()); } initCursor(); updateOutputs(); if (m_outputs.isEmpty()) { qCWarning(KWIN_DRM) << "No outputs, cannot render, will terminate now"; emit initFailed(); return; } // 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->enable(); } } setReady(true); } void DrmBackend::updateOutputs() { if (m_fd < 0) { return; } ScopedDrmPointer<_drmModeRes, &drmModeFreeResources> resources(drmModeGetResources(m_fd)); if (!resources) { qCWarning(KWIN_DRM) << "drmModeGetResources failed"; return; } QVector connectedOutputs; QVector pendingConnectors; // split up connected connectors in already or not yet assigned ones for (DrmConnector *con : qAsConst(m_connectors)) { if (!con->isConnected()) { continue; } if (DrmOutput *o = findOutput(con->id())) { connectedOutputs << o; } else { pendingConnectors << con; } } // check for outputs which got removed auto it = m_outputs.begin(); while (it != m_outputs.end()) { if (connectedOutputs.contains(*it)) { it++; continue; } DrmOutput *removed = *it; it = m_outputs.erase(it); m_enabledOutputs.removeOne(removed); emit outputRemoved(removed); delete removed; } // now check new connections for (DrmConnector *con : qAsConst(pendingConnectors)) { ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> connector(drmModeGetConnector(m_fd, con->id())); if (!connector) { continue; } if (connector->count_modes == 0) { continue; } bool outputDone = false; QVector encoders = con->encoders(); for (auto encId : qAsConst(encoders)) { ScopedDrmPointer<_drmModeEncoder, &drmModeFreeEncoder> encoder(drmModeGetEncoder(m_fd, encId)); if (!encoder) { continue; } for (DrmCrtc *crtc : qAsConst(m_crtcs)) { if (!(encoder->possible_crtcs & (1 << crtc->resIndex()))) { continue; } // check if crtc isn't used yet -- currently we don't allow multiple outputs on one crtc (cloned mode) auto it = std::find_if(connectedOutputs.constBegin(), connectedOutputs.constEnd(), [crtc] (DrmOutput *o) { return o->m_crtc == crtc; } ); if (it != connectedOutputs.constEnd()) { continue; } // we found a suitable encoder+crtc // TODO: we could avoid these lib drm calls if we store all struct data in DrmCrtc and DrmConnector in the beginning ScopedDrmPointer<_drmModeCrtc, &drmModeFreeCrtc> modeCrtc(drmModeGetCrtc(m_fd, crtc->id())); if (!modeCrtc) { continue; } DrmOutput *output = new DrmOutput(this); con->setOutput(output); output->m_conn = con; crtc->setOutput(output); output->m_crtc = crtc; connect(output, &DrmOutput::dpmsChanged, this, &DrmBackend::outputDpmsChanged); if (modeCrtc->mode_valid) { output->m_mode = modeCrtc->mode; } else { output->m_mode = connector->modes[0]; } qCDebug(KWIN_DRM) << "For new output use mode " << output->m_mode.name; if (!output->init(connector.data())) { qCWarning(KWIN_DRM) << "Failed to create output for connector " << con->id(); delete output; continue; } if (!output->initCursor(m_cursorSize)) { setSoftWareCursor(true); } qCDebug(KWIN_DRM) << "Found new output with uuid" << output->uuid(); connectedOutputs << output; emit outputAdded(output); outputDone = true; break; } if (outputDone) { break; } } } std::sort(connectedOutputs.begin(), connectedOutputs.end(), [] (DrmOutput *a, DrmOutput *b) { return a->m_conn->id() < b->m_conn->id(); }); m_outputs = connectedOutputs; m_enabledOutputs = connectedOutputs; readOutputsConfiguration(); if (!m_outputs.isEmpty()) { emit screensQueried(); } } void DrmBackend::readOutputsConfiguration() { if (m_outputs.isEmpty()) { return; } const QByteArray uuid = generateOutputConfigurationUuid(); const auto outputGroup = kwinApp()->config()->group("DrmOutputs"); const auto configGroup = outputGroup.group(uuid); // default position goes from left to right QPoint pos(0, 0); for (auto it = m_outputs.begin(); it != m_outputs.end(); ++it) { qCDebug(KWIN_DRM) << "Reading output configuration for [" << uuid << "] ["<< (*it)->uuid() << "]"; const auto outputConfig = configGroup.group((*it)->uuid()); (*it)->setGlobalPos(outputConfig.readEntry("Position", pos)); // TODO: add mode (*it)->setScale(outputConfig.readEntry("Scale", 1.0)); pos.setX(pos.x() + (*it)->geometry().width()); } } QByteArray DrmBackend::generateOutputConfigurationUuid() const { auto it = m_outputs.constBegin(); if (m_outputs.size() == 1) { // special case: one output return (*it)->uuid(); } QCryptographicHash hash(QCryptographicHash::Md5); for (; it != m_outputs.constEnd(); ++it) { hash.addData((*it)->uuid()); } return hash.result().toHex().left(10); } void DrmBackend::configurationChangeRequested(KWayland::Server::OutputConfigurationInterface *config) { const auto changes = config->changes(); bool countChanged = false; //process all non-disabling changes for (auto it = changes.begin(); it != changes.end(); it++) { KWayland::Server::OutputChangeSet *changeset = it.value(); auto drmoutput = findOutput(it.key()->uuid()); if (drmoutput == nullptr) { qCWarning(KWIN_DRM) << "Could NOT find DrmOutput matching " << it.key()->uuid(); continue; } if (changeset->enabledChanged() && changeset->enabled() == KWayland::Server::OutputDeviceInterface::Enablement::Enabled) { drmoutput->setEnabled(true); m_enabledOutputs << drmoutput; emit outputAdded(drmoutput); countChanged = true; } drmoutput->setChanges(changeset); } //process any disable requests for (auto it = changes.begin(); it != changes.end(); it++) { KWayland::Server::OutputChangeSet *changeset = it.value(); if (changeset->enabledChanged() && changeset->enabled() == KWayland::Server::OutputDeviceInterface::Enablement::Disabled) { if (m_enabledOutputs.count() == 1) { qCWarning(KWIN_DRM) << "Not disabling final screen" << it.key()->uuid(); continue; } auto drmoutput = findOutput(it.key()->uuid()); if (drmoutput == nullptr) { qCWarning(KWIN_DRM) << "Could NOT find DrmOutput matching " << it.key()->uuid(); continue; } drmoutput->setEnabled(false); m_enabledOutputs.removeOne(drmoutput); emit outputRemoved(drmoutput); countChanged = true; } } if (countChanged) { emit screensQueried(); } else { emit screens()->changed(); } // KCoreAddons needs kwayland's 2b3f9509ac1 to not crash if (KCoreAddons::version() >= QT_VERSION_CHECK(5, 39, 0)) { config->setApplied(); } } DrmOutput *DrmBackend::findOutput(quint32 connector) { auto it = std::find_if(m_outputs.constBegin(), m_outputs.constEnd(), [connector] (DrmOutput *o) { return o->m_conn->id() == connector; }); if (it != m_outputs.constEnd()) { return *it; } return nullptr; } DrmOutput *DrmBackend::findOutput(const QByteArray &uuid) { auto it = std::find_if(m_outputs.constBegin(), m_outputs.constEnd(), [uuid] (DrmOutput *o) { return o->m_uuid == uuid; }); if (it != m_outputs.constEnd()) { return *it; } return nullptr; } void DrmBackend::present(DrmBuffer *buffer, DrmOutput *output) { if (!buffer || buffer->bufferId() == 0) { if (m_deleteBufferAfterPageFlip) { delete buffer; } return; } if (output->present(buffer)) { m_pageFlipsPending++; if (m_pageFlipsPending == 1 && Compositor::self()) { Compositor::self()->aboutToSwapBuffers(); } } else if (m_deleteBufferAfterPageFlip) { delete buffer; } } void DrmBackend::initCursor() { m_cursorEnabled = waylandServer()->seat()->hasPointer(); connect(waylandServer()->seat(), &KWayland::Server::SeatInterface::hasPointerChanged, this, [this] { m_cursorEnabled = waylandServer()->seat()->hasPointer(); if (usesSoftwareCursor()) { return; } for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { if (m_cursorEnabled) { (*it)->showCursor(); } else { (*it)->hideCursor(); } } } ); uint64_t capability = 0; QSize cursorSize; if (drmGetCap(m_fd, DRM_CAP_CURSOR_WIDTH, &capability) == 0) { cursorSize.setWidth(capability); } else { cursorSize.setWidth(64); } if (drmGetCap(m_fd, DRM_CAP_CURSOR_HEIGHT, &capability) == 0) { cursorSize.setHeight(capability); } else { cursorSize.setHeight(64); } m_cursorSize = cursorSize; // now we have screens and can set cursors, so start tracking connect(this, &DrmBackend::cursorChanged, this, &DrmBackend::updateCursor); connect(Cursor::self(), &Cursor::posChanged, this, &DrmBackend::moveCursor); } void DrmBackend::setCursor() { if (m_cursorEnabled) { for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { (*it)->showCursor(); } } markCursorAsRendered(); } void DrmBackend::updateCursor() { if (usesSoftwareCursor()) { return; } if (isCursorHidden()) { return; } const QImage &cursorImage = softwareCursor(); if (cursorImage.isNull()) { doHideCursor(); return; } for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { (*it)->updateCursor(); } setCursor(); moveCursor(); } void DrmBackend::doShowCursor() { updateCursor(); } void DrmBackend::doHideCursor() { if (!m_cursorEnabled) { return; } for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { (*it)->hideCursor(); } } void DrmBackend::moveCursor() { if (!m_cursorEnabled || isCursorHidden()) { return; } for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { (*it)->moveCursor(Cursor::pos()); } } Screens *DrmBackend::createScreens(QObject *parent) { return new DrmScreens(this, parent); } QPainterBackend *DrmBackend::createQPainterBackend() { m_deleteBufferAfterPageFlip = false; return new DrmQPainterBackend(this); } OpenGLBackend *DrmBackend::createOpenGLBackend() { #if HAVE_GBM m_deleteBufferAfterPageFlip = true; return new EglGbmBackend(this); #else return Platform::createOpenGLBackend(); #endif } DrmDumbBuffer *DrmBackend::createBuffer(const QSize &size) { - DrmDumbBuffer *b = new DrmDumbBuffer(this, size); + DrmDumbBuffer *b = new DrmDumbBuffer(m_fd, size); return b; } #if HAVE_GBM DrmSurfaceBuffer *DrmBackend::createBuffer(const std::shared_ptr &surface) { - DrmSurfaceBuffer *b = new DrmSurfaceBuffer(this, surface); + DrmSurfaceBuffer *b = new DrmSurfaceBuffer(m_fd, surface); return b; } #endif void DrmBackend::outputDpmsChanged() { if (m_enabledOutputs.isEmpty()) { return; } bool enabled = false; for (auto it = m_enabledOutputs.constBegin(); it != m_enabledOutputs.constEnd(); ++it) { enabled = enabled || (*it)->isDpmsEnabled(); } setOutputsEnabled(enabled); } QVector DrmBackend::supportedCompositors() const { #if HAVE_GBM return QVector{OpenGLCompositing, QPainterCompositing}; #else return QVector{QPainterCompositing}; #endif } QString DrmBackend::supportInformation() const { QString supportInfo; QDebug s(&supportInfo); s.nospace(); s << "Name: " << "DRM" << endl; s << "Active: " << m_active << endl; s << "Atomic Mode Setting: " << m_atomicModeSetting << endl; return supportInfo; } } diff --git a/plugins/platforms/drm/drm_buffer.cpp b/plugins/platforms/drm/drm_buffer.cpp index a28c45942..b640c593c 100644 --- a/plugins/platforms/drm/drm_buffer.cpp +++ b/plugins/platforms/drm/drm_buffer.cpp @@ -1,107 +1,107 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 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 "drm_buffer.h" -#include "drm_backend.h" #include "logging.h" // system #include #include // drm #include +#include namespace KWin { -DrmBuffer:: DrmBuffer(DrmBackend *backend) - : m_backend(backend) +DrmBuffer:: DrmBuffer(int fd) + : m_fd(fd) { } // DrmDumbBuffer -DrmDumbBuffer::DrmDumbBuffer(DrmBackend *backend, const QSize &size) - : DrmBuffer(backend) +DrmDumbBuffer::DrmDumbBuffer(int fd, const QSize &size) + : DrmBuffer(fd) { m_size = size; drm_mode_create_dumb createArgs; memset(&createArgs, 0, sizeof createArgs); createArgs.bpp = 32; createArgs.width = size.width(); createArgs.height = size.height(); - if (drmIoctl(m_backend->fd(), DRM_IOCTL_MODE_CREATE_DUMB, &createArgs) != 0) { + if (drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &createArgs) != 0) { qCWarning(KWIN_DRM) << "DRM_IOCTL_MODE_CREATE_DUMB failed"; return; } m_handle = createArgs.handle; m_bufferSize = createArgs.size; m_stride = createArgs.pitch; - if (drmModeAddFB(m_backend->fd(), size.width(), size.height(), 24, 32, + if (drmModeAddFB(fd, size.width(), size.height(), 24, 32, m_stride, createArgs.handle, &m_bufferId) != 0) { qCWarning(KWIN_DRM) << "drmModeAddFB failed with errno" << errno; } } DrmDumbBuffer::~DrmDumbBuffer() { if (m_bufferId) { - drmModeRmFB(m_backend->fd(), m_bufferId); + drmModeRmFB(fd(), m_bufferId); } delete m_image; if (m_memory) { munmap(m_memory, m_bufferSize); } if (m_handle) { drm_mode_destroy_dumb destroyArgs; destroyArgs.handle = m_handle; - drmIoctl(m_backend->fd(), DRM_IOCTL_MODE_DESTROY_DUMB, &destroyArgs); + drmIoctl(fd(), DRM_IOCTL_MODE_DESTROY_DUMB, &destroyArgs); } } bool DrmDumbBuffer::needsModeChange(DrmBuffer *b) const { if (DrmDumbBuffer *db = dynamic_cast(b)) { return m_stride != db->stride(); } else { return true; } } bool DrmDumbBuffer::map(QImage::Format format) { if (!m_handle || !m_bufferId) { return false; } drm_mode_map_dumb mapArgs; memset(&mapArgs, 0, sizeof mapArgs); mapArgs.handle = m_handle; - if (drmIoctl(m_backend->fd(), DRM_IOCTL_MODE_MAP_DUMB, &mapArgs) != 0) { + if (drmIoctl(fd(), DRM_IOCTL_MODE_MAP_DUMB, &mapArgs) != 0) { return false; } - void *address = mmap(nullptr, m_bufferSize, PROT_WRITE, MAP_SHARED, m_backend->fd(), mapArgs.offset); + void *address = mmap(nullptr, m_bufferSize, PROT_WRITE, MAP_SHARED, fd(), mapArgs.offset); if (address == MAP_FAILED) { return false; } m_memory = address; m_image = new QImage((uchar*)m_memory, m_size.width(), m_size.height(), m_stride, format); return !m_image->isNull(); } } diff --git a/plugins/platforms/drm/drm_buffer.h b/plugins/platforms/drm/drm_buffer.h index ab50f00ba..7af2ef913 100644 --- a/plugins/platforms/drm/drm_buffer.h +++ b/plugins/platforms/drm/drm_buffer.h @@ -1,86 +1,88 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 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_DRM_BUFFER_H #define KWIN_DRM_BUFFER_H #include #include namespace KWin { -class DrmBackend; - class DrmBuffer { public: - DrmBuffer(DrmBackend *backend); + DrmBuffer(int fd); virtual ~DrmBuffer() = default; virtual bool needsModeChange(DrmBuffer *b) const {Q_UNUSED(b) return false;} quint32 bufferId() const { return m_bufferId; } const QSize &size() const { return m_size; } virtual void releaseGbm() {} + int fd() const { + return m_fd; + } + protected: - DrmBackend *m_backend; quint32 m_bufferId = 0; QSize m_size; + int m_fd; }; class DrmDumbBuffer : public DrmBuffer { public: - DrmDumbBuffer(DrmBackend *backend, const QSize &size); + DrmDumbBuffer(int fd, const QSize &size); ~DrmDumbBuffer(); bool needsModeChange(DrmBuffer *b) const override; bool map(QImage::Format format = QImage::Format_RGB32); quint32 handle() const { return m_handle; } QImage *image() const { return m_image; } quint32 stride() const { return m_stride; } private: quint32 m_handle = 0; quint64 m_bufferSize = 0; void *m_memory = nullptr; QImage *m_image = nullptr; quint32 m_stride = 0; }; } #endif diff --git a/plugins/platforms/drm/drm_buffer_gbm.cpp b/plugins/platforms/drm/drm_buffer_gbm.cpp index 6959d1bd9..cbc0d3abd 100644 --- a/plugins/platforms/drm/drm_buffer_gbm.cpp +++ b/plugins/platforms/drm/drm_buffer_gbm.cpp @@ -1,68 +1,68 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright 2017 Roman Gilg Copyright 2015 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 "drm_backend.h" #include "drm_buffer_gbm.h" #include "gbm_surface.h" #include "logging.h" // system #include #include // drm #include +#include #include namespace KWin { // DrmSurfaceBuffer -DrmSurfaceBuffer::DrmSurfaceBuffer(DrmBackend *backend, const std::shared_ptr &surface) - : DrmBuffer(backend) +DrmSurfaceBuffer::DrmSurfaceBuffer(int fd, const std::shared_ptr &surface) + : DrmBuffer(fd) , m_surface(surface) { m_bo = m_surface->lockFrontBuffer(); if (!m_bo) { qCWarning(KWIN_DRM) << "Locking front buffer failed"; return; } m_size = QSize(gbm_bo_get_width(m_bo), gbm_bo_get_height(m_bo)); - if (drmModeAddFB(m_backend->fd(), m_size.width(), m_size.height(), 24, 32, gbm_bo_get_stride(m_bo), gbm_bo_get_handle(m_bo).u32, &m_bufferId) != 0) { + if (drmModeAddFB(fd, m_size.width(), m_size.height(), 24, 32, gbm_bo_get_stride(m_bo), gbm_bo_get_handle(m_bo).u32, &m_bufferId) != 0) { qCWarning(KWIN_DRM) << "drmModeAddFB failed"; } gbm_bo_set_user_data(m_bo, this, nullptr); } DrmSurfaceBuffer::~DrmSurfaceBuffer() { if (m_bufferId) { - drmModeRmFB(m_backend->fd(), m_bufferId); + drmModeRmFB(fd(), m_bufferId); } releaseGbm(); } void DrmSurfaceBuffer::releaseGbm() { m_surface->releaseBuffer(m_bo); m_bo = nullptr; } } diff --git a/plugins/platforms/drm/drm_buffer_gbm.h b/plugins/platforms/drm/drm_buffer_gbm.h index d26f66556..2b39952aa 100644 --- a/plugins/platforms/drm/drm_buffer_gbm.h +++ b/plugins/platforms/drm/drm_buffer_gbm.h @@ -1,63 +1,62 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright 2017 Roman Gilg Copyright 2015 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_DRM_BUFFER_GBM_H #define KWIN_DRM_BUFFER_GBM_H #include "drm_buffer.h" #include struct gbm_bo; namespace KWin { -class DrmBackend; class GbmSurface; class DrmSurfaceBuffer : public DrmBuffer { public: - DrmSurfaceBuffer(DrmBackend *backend, const std::shared_ptr &surface); + DrmSurfaceBuffer(int fd, const std::shared_ptr &surface); ~DrmSurfaceBuffer(); bool needsModeChange(DrmBuffer *b) const override { if (DrmSurfaceBuffer *sb = dynamic_cast(b)) { return hasBo() != sb->hasBo(); } else { return true; } } bool hasBo() const { return m_bo != nullptr; } void releaseGbm() override; private: std::shared_ptr m_surface; gbm_bo *m_bo = nullptr; }; } #endif diff --git a/plugins/platforms/drm/drm_object.cpp b/plugins/platforms/drm/drm_object.cpp index 48d297fa9..e67b581b5 100644 --- a/plugins/platforms/drm/drm_object.cpp +++ b/plugins/platforms/drm/drm_object.cpp @@ -1,156 +1,155 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Roman Gilg 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_object.h" -#include "drm_backend.h" #include "logging.h" namespace KWin { /* * Defintions for class DrmObject */ -DrmObject::DrmObject(uint32_t object_id, DrmBackend *backend) - : m_backend(backend) +DrmObject::DrmObject(uint32_t object_id, int fd) + : m_fd(fd) , m_id(object_id) { } DrmObject::~DrmObject() { foreach(Property* p, m_props) delete p; } void DrmObject::setPropertyNames(QVector &&vector) { m_propsNames = std::move(vector); m_props.fill(nullptr, m_propsNames.size()); } void DrmObject::initProp(int n, drmModeObjectProperties *properties, QVector enumNames) { for (unsigned int i = 0; i < properties->count_props; ++i) { - drmModePropertyRes *prop = drmModeGetProperty(m_backend->fd(), properties->props[i]); + drmModePropertyRes *prop = drmModeGetProperty(fd(), properties->props[i]); if (!prop) { continue; } if (prop->name == m_propsNames[n]) { qCDebug(KWIN_DRM).nospace() << m_id << ": " << prop->name << "' (id " << prop->prop_id << "): " << properties->prop_values[i]; m_props[n] = new Property(prop, properties->prop_values[i], enumNames); } drmModeFreeProperty(prop); } } bool DrmObject::atomicAddProperty(drmModeAtomicReq *req, Property *property) { if (drmModeAtomicAddProperty(req, m_id, property->propId(), property->value()) <= 0) { qCWarning(KWIN_DRM) << "Adding property" << property->name() << "to atomic commit failed for object" << this; return false; } return true; } bool DrmObject::atomicPopulate(drmModeAtomicReq *req) { bool ret = true; for (int i = 0; i < m_props.size(); i++) { auto property = m_props.at(i); if (!property) { continue; } ret &= atomicAddProperty(req, property); } if (!ret) { qCWarning(KWIN_DRM) << "Failed to populate atomic plane" << m_id; return false; } return true; } /* * Defintions for struct Prop */ DrmObject::Property::Property(drmModePropertyRes *prop, uint64_t val, QVector enumNames) : m_propId(prop->prop_id) , m_propName(prop->name) , m_value(val) { if (!enumNames.isEmpty()) { qCDebug(KWIN_DRM) << m_propName << " has enums:" << enumNames; m_enumNames = enumNames; initEnumMap(prop); } } DrmObject::Property::~Property() = default; void DrmObject::Property::initEnumMap(drmModePropertyRes *prop) { if (!((prop->flags & DRM_MODE_PROP_ENUM) || (prop->flags & DRM_MODE_PROP_BITMASK)) || prop->count_enums < 1) { qCWarning(KWIN_DRM) << "Property '" << prop->name << "' ( id =" << m_propId << ") should be enum valued, but it is not."; return; } int nameCount = m_enumNames.size(); m_enumMap.resize(nameCount); qCDebug(KWIN_DRM).nospace() << "Test all " << prop->count_enums << " possible enums" <<":"; for (int i = 0; i < prop->count_enums; i++) { struct drm_mode_property_enum *en = &prop->enums[i]; int j = 0; while (QByteArray(en->name) != m_enumNames[j]) { j++; if (j == nameCount) { break; } } if (j == nameCount) { qCWarning(KWIN_DRM).nospace() << m_propName << " has unrecognized enum '" << en->name << "'"; } else { qCDebug(KWIN_DRM).nospace() << "Enum '" << en->name << "': runtime-value = " << en->value; m_enumMap[j] = en->value; } } if (KWIN_DRM().isDebugEnabled()) { for (int i = 0; i < m_enumMap.size(); i++) { if (m_value == m_enumMap[i]) { qCDebug(KWIN_DRM) << "=>" << m_propName << "with mapped enum value" << m_enumNames[i]; } } } } } diff --git a/plugins/platforms/drm/drm_object.h b/plugins/platforms/drm/drm_object.h index 50ec56f34..450dd491f 100644 --- a/plugins/platforms/drm/drm_object.h +++ b/plugins/platforms/drm/drm_object.h @@ -1,132 +1,136 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Roman Gilg 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_DRM_OBJECT_H #define KWIN_DRM_OBJECT_H #include #include // drm #include namespace KWin { class DrmBackend; class DrmOutput; class DrmObject { public: // creates drm object by its id delivered by the kernel - DrmObject(uint32_t object_id, DrmBackend *backend); + DrmObject(uint32_t object_id, int fd); - virtual ~DrmObject() = 0; + virtual ~DrmObject(); virtual bool atomicInit() = 0; uint32_t id() const { return m_id; } DrmOutput *output() const { return m_output; } void setOutput(DrmOutput* output) { m_output = output; } bool propHasEnum(int prop, uint64_t value) const { auto property = m_props.at(prop); return property ? property->hasEnum(value) : false; } void setValue(int prop, uint64_t new_value) { Q_ASSERT(prop < m_props.size()); auto property = m_props.at(prop); if (property) { property->setValue(new_value); } } + int fd() const { + return m_fd; + } + virtual bool atomicPopulate(drmModeAtomicReq *req); protected: virtual bool initProps() = 0; // only derived classes know names and quantity of properties void setPropertyNames(QVector &&vector); void initProp(int n, drmModeObjectProperties *properties, QVector enumNames = QVector(0)); class Property; bool atomicAddProperty(drmModeAtomicReq *req, Property *property); - DrmBackend *m_backend; - const uint32_t m_id = 0; + int m_fd; + const uint32_t m_id; DrmOutput *m_output = nullptr; // for comparision with received name of DRM object QVector m_props; class Property { public: Property(drmModePropertyRes *prop, uint64_t val, QVector enumNames); virtual ~Property(); void initEnumMap(drmModePropertyRes *prop); uint64_t enumMap(int n) { return m_enumMap[n]; // TODO: test on index out of bounds? } bool hasEnum(uint64_t value) const { return m_enumMap.contains(value); } uint32_t propId() const { return m_propId; } uint64_t value() const { return m_value; } void setValue(uint64_t new_value) { m_value = new_value; } const QByteArray &name() const { return m_propName; } private: uint32_t m_propId = 0; QByteArray m_propName; uint64_t m_value = 0; QVector m_enumMap; QVector m_enumNames; }; private: QVector m_propsNames; }; } #endif diff --git a/plugins/platforms/drm/drm_object_connector.cpp b/plugins/platforms/drm/drm_object_connector.cpp index f671f0c3c..30b6e584a 100644 --- a/plugins/platforms/drm/drm_object_connector.cpp +++ b/plugins/platforms/drm/drm_object_connector.cpp @@ -1,81 +1,80 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Roman Gilg 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_object_connector.h" -#include "drm_backend.h" #include "drm_pointer.h" #include "logging.h" namespace KWin { -DrmConnector::DrmConnector(uint32_t connector_id, DrmBackend *backend) - : DrmObject(connector_id, backend) +DrmConnector::DrmConnector(uint32_t connector_id, int fd) + : DrmObject(connector_id, fd) { - ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> con(drmModeGetConnector(backend->fd(), connector_id)); + ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> con(drmModeGetConnector(fd, connector_id)); if (!con) { return; } for (int i = 0; i < con->count_encoders; ++i) { m_encoders << con->encoders[i]; } } DrmConnector::~DrmConnector() = default; bool DrmConnector::atomicInit() { qCDebug(KWIN_DRM) << "Creating connector" << m_id; if (!initProps()) { return false; } return true; } bool DrmConnector::initProps() { setPropertyNames( { QByteArrayLiteral("CRTC_ID"), }); - drmModeObjectProperties *properties = drmModeObjectGetProperties(m_backend->fd(), m_id, DRM_MODE_OBJECT_CONNECTOR); + drmModeObjectProperties *properties = drmModeObjectGetProperties(fd(), m_id, DRM_MODE_OBJECT_CONNECTOR); if (!properties) { qCWarning(KWIN_DRM) << "Failed to get properties for connector " << m_id ; return false; } int propCount = int(PropertyIndex::Count); for (int j = 0; j < propCount; ++j) { initProp(j, properties); } drmModeFreeObjectProperties(properties); return true; } bool DrmConnector::isConnected() { - ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> con(drmModeGetConnector(m_backend->fd(), m_id)); + ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> con(drmModeGetConnector(fd(), m_id)); if (!con) { return false; } return con->connection == DRM_MODE_CONNECTED; } } diff --git a/plugins/platforms/drm/drm_object_connector.h b/plugins/platforms/drm/drm_object_connector.h index 0f6fcdbd4..7cd240629 100644 --- a/plugins/platforms/drm/drm_object_connector.h +++ b/plugins/platforms/drm/drm_object_connector.h @@ -1,57 +1,57 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Roman Gilg 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_DRM_OBJECT_CONNECTOR_H #define KWIN_DRM_OBJECT_CONNECTOR_H #include "drm_object.h" namespace KWin { class DrmConnector : public DrmObject { public: - DrmConnector(uint32_t connector_id, DrmBackend *backend); + DrmConnector(uint32_t connector_id, int fd); virtual ~DrmConnector(); bool atomicInit(); enum class PropertyIndex { CrtcId = 0, Count }; QVector encoders() { return m_encoders; } bool initProps(); bool isConnected(); private: QVector m_encoders; }; } #endif diff --git a/plugins/platforms/drm/drm_object_crtc.cpp b/plugins/platforms/drm/drm_object_crtc.cpp index d44ca787b..851b2486e 100644 --- a/plugins/platforms/drm/drm_object_crtc.cpp +++ b/plugins/platforms/drm/drm_object_crtc.cpp @@ -1,109 +1,110 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Roman Gilg 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_object_crtc.h" #include "drm_backend.h" #include "drm_output.h" #include "drm_buffer.h" #include "logging.h" namespace KWin { DrmCrtc::DrmCrtc(uint32_t crtc_id, DrmBackend *backend, int resIndex) - : DrmObject(crtc_id, backend), - m_resIndex(resIndex) + : DrmObject(crtc_id, backend->fd()), + m_resIndex(resIndex), + m_backend(backend) { } DrmCrtc::~DrmCrtc() { } bool DrmCrtc::atomicInit() { qCDebug(KWIN_DRM) << "Atomic init for CRTC:" << resIndex() << "id:" << m_id; if (!initProps()) { return false; } return true; } bool DrmCrtc::initProps() { setPropertyNames({ QByteArrayLiteral("MODE_ID"), QByteArrayLiteral("ACTIVE"), }); - drmModeObjectProperties *properties = drmModeObjectGetProperties(m_backend->fd(), m_id, DRM_MODE_OBJECT_CRTC); + drmModeObjectProperties *properties = drmModeObjectGetProperties(fd(), m_id, DRM_MODE_OBJECT_CRTC); if (!properties) { qCWarning(KWIN_DRM) << "Failed to get properties for crtc " << m_id ; return false; } int propCount = int(PropertyIndex::Count); for (int j = 0; j < propCount; ++j) { initProp(j, properties); } drmModeFreeObjectProperties(properties); return true; } void DrmCrtc::flipBuffer() { if (m_currentBuffer && m_backend->deleteBufferAfterPageFlip() && m_currentBuffer != m_nextBuffer) { delete m_currentBuffer; } m_currentBuffer = m_nextBuffer; m_nextBuffer = nullptr; delete m_blackBuffer; m_blackBuffer = nullptr; } bool DrmCrtc::blank() { if (!m_output) { return false; } if (!m_blackBuffer) { DrmDumbBuffer *blackBuffer = m_backend->createBuffer(m_output->pixelSize()); if (!blackBuffer->map()) { delete blackBuffer; return false; } blackBuffer->image()->fill(Qt::black); m_blackBuffer = blackBuffer; } if (m_output->setModeLegacy(m_blackBuffer)) { if (m_currentBuffer && m_backend->deleteBufferAfterPageFlip()) { delete m_currentBuffer; delete m_nextBuffer; } m_currentBuffer = nullptr; m_nextBuffer = nullptr; return true; } return false; } } diff --git a/plugins/platforms/drm/drm_object_crtc.h b/plugins/platforms/drm/drm_object_crtc.h index c5fc37d21..b4062590f 100644 --- a/plugins/platforms/drm/drm_object_crtc.h +++ b/plugins/platforms/drm/drm_object_crtc.h @@ -1,77 +1,78 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Roman Gilg 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_DRM_OBJECT_CRTC_H #define KWIN_DRM_OBJECT_CRTC_H #include "drm_object.h" namespace KWin { class DrmBackend; class DrmBuffer; class DrmDumbBuffer; class DrmCrtc : public DrmObject { public: DrmCrtc(uint32_t crtc_id, DrmBackend *backend, int resIndex); virtual ~DrmCrtc(); bool atomicInit(); enum class PropertyIndex { ModeId = 0, Active, Count }; bool initProps(); int resIndex() const { return m_resIndex; } DrmBuffer *current() { return m_currentBuffer; } DrmBuffer *next() { return m_nextBuffer; } void setNext(DrmBuffer *buffer) { m_nextBuffer = buffer; } void flipBuffer(); bool blank(); private: int m_resIndex; DrmBuffer *m_currentBuffer = nullptr; DrmBuffer *m_nextBuffer = nullptr; DrmDumbBuffer *m_blackBuffer = nullptr; + DrmBackend *m_backend; }; } #endif diff --git a/plugins/platforms/drm/drm_object_plane.cpp b/plugins/platforms/drm/drm_object_plane.cpp index f9efc6161..929d27d72 100644 --- a/plugins/platforms/drm/drm_object_plane.cpp +++ b/plugins/platforms/drm/drm_object_plane.cpp @@ -1,201 +1,200 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Roman Gilg 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_object_plane.h" -#include "drm_backend.h" #include "drm_buffer.h" #include "drm_pointer.h" #include "logging.h" namespace KWin { -DrmPlane::DrmPlane(uint32_t plane_id, DrmBackend *backend) - : DrmObject(plane_id, backend) +DrmPlane::DrmPlane(uint32_t plane_id, int fd) + : DrmObject(plane_id, fd) { } DrmPlane::~DrmPlane() { delete m_current; delete m_next; } bool DrmPlane::atomicInit() { qCDebug(KWIN_DRM) << "Atomic init for plane:" << m_id; - ScopedDrmPointer<_drmModePlane, &drmModeFreePlane> p(drmModeGetPlane(m_backend->fd(), m_id)); + ScopedDrmPointer<_drmModePlane, &drmModeFreePlane> p(drmModeGetPlane(fd(), m_id)); if (!p) { qCWarning(KWIN_DRM) << "Failed to get kernel plane" << m_id; return false; } m_possibleCrtcs = p->possible_crtcs; int count_formats = p->count_formats; m_formats.resize(count_formats); for (int i = 0; i < count_formats; i++) { m_formats[i] = p->formats[i]; } if (!initProps()) { return false; } return true; } bool DrmPlane::initProps() { setPropertyNames( { QByteArrayLiteral("type"), QByteArrayLiteral("SRC_X"), QByteArrayLiteral("SRC_Y"), QByteArrayLiteral("SRC_W"), QByteArrayLiteral("SRC_H"), QByteArrayLiteral("CRTC_X"), QByteArrayLiteral("CRTC_Y"), QByteArrayLiteral("CRTC_W"), QByteArrayLiteral("CRTC_H"), QByteArrayLiteral("FB_ID"), QByteArrayLiteral("CRTC_ID"), QByteArrayLiteral("rotation") }); QVector typeNames = { QByteArrayLiteral("Primary"), QByteArrayLiteral("Cursor"), QByteArrayLiteral("Overlay"), }; const QVector rotationNames{ QByteArrayLiteral("rotate-0"), QByteArrayLiteral("rotate-90"), QByteArrayLiteral("rotate-180"), QByteArrayLiteral("rotate-270"), QByteArrayLiteral("reflect-x"), QByteArrayLiteral("reflect-y") }; - drmModeObjectProperties *properties = drmModeObjectGetProperties(m_backend->fd(), m_id, DRM_MODE_OBJECT_PLANE); + drmModeObjectProperties *properties = drmModeObjectGetProperties(fd(), m_id, DRM_MODE_OBJECT_PLANE); if (!properties){ qCWarning(KWIN_DRM) << "Failed to get properties for plane " << m_id ; return false; } int propCount = int(PropertyIndex::Count); for (int j = 0; j < propCount; ++j) { if (j == int(PropertyIndex::Type)) { initProp(j, properties, typeNames); } else if (j == int(PropertyIndex::Rotation)) { initProp(j, properties, rotationNames); m_supportedTransformations = Transformations(); auto testTransform = [j, this] (uint64_t value, Transformation t) { if (propHasEnum(j, value)) { m_supportedTransformations |= t; } }; testTransform(0, Transformation::Rotate0); testTransform(1, Transformation::Rotate90); testTransform(2, Transformation::Rotate180); testTransform(3, Transformation::Rotate270); testTransform(4, Transformation::ReflectX); testTransform(5, Transformation::ReflectY); qCDebug(KWIN_DRM) << "Supported Transformations: " << m_supportedTransformations << " on plane " << m_id; } else { initProp(j, properties); } } drmModeFreeObjectProperties(properties); return true; } DrmPlane::TypeIndex DrmPlane::type() { auto property = m_props.at(int(PropertyIndex::Type)); if (!property) { return TypeIndex::Overlay; } int typeCount = int(TypeIndex::Count); for (int i = 0; i < typeCount; i++) { if (property->enumMap(i) == property->value()) { return TypeIndex(i); } } return TypeIndex::Overlay; } void DrmPlane::setNext(DrmBuffer *b) { if (auto property = m_props.at(int(PropertyIndex::FbId))) { property->setValue(b ? b->bufferId() : 0); } m_next = b; } void DrmPlane::setTransformation(Transformations t) { if (auto property = m_props.at(int(PropertyIndex::Rotation))) { property->setValue(int(t)); } } DrmPlane::Transformations DrmPlane::transformation() { if (auto property = m_props.at(int(PropertyIndex::Rotation))) { return Transformations(int(property->value())); } return Transformations(Transformation::Rotate0); } bool DrmPlane::atomicPopulate(drmModeAtomicReq *req) { bool ret = true; for (int i = 1; i < m_props.size(); i++) { auto property = m_props.at(i); if (!property) { continue; } ret &= atomicAddProperty(req, property); } if (!ret) { qCWarning(KWIN_DRM) << "Failed to populate atomic plane" << m_id; return false; } return true; } void DrmPlane::flipBuffer() { m_current = m_next; m_next = nullptr; } void DrmPlane::flipBufferWithDelete() { if (m_current != m_next) { delete m_current; } flipBuffer(); } } diff --git a/plugins/platforms/drm/drm_object_plane.h b/plugins/platforms/drm/drm_object_plane.h index 8ddc60c29..cd6f739c6 100644 --- a/plugins/platforms/drm/drm_object_plane.h +++ b/plugins/platforms/drm/drm_object_plane.h @@ -1,122 +1,122 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Roman Gilg 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_DRM_OBJECT_PLANE_H #define KWIN_DRM_OBJECT_PLANE_H #include "drm_object.h" // drm #include namespace KWin { class DrmBuffer; class DrmPlane : public DrmObject { public: - DrmPlane(uint32_t plane_id, DrmBackend *backend); + DrmPlane(uint32_t plane_id, int fd); ~DrmPlane(); enum class PropertyIndex { Type = 0, SrcX, SrcY, SrcW, SrcH, CrtcX, CrtcY, CrtcW, CrtcH, FbId, CrtcId, Rotation, Count }; enum class TypeIndex { Primary = 0, Cursor, Overlay, Count }; enum class Transformation { Rotate0 = 1 << 0, Rotate90 = 1 << 1, Rotate180 = 1 << 2, Rotate270 = 1 << 3, ReflectX = 1 << 4, ReflectY = 1 << 5 }; Q_DECLARE_FLAGS(Transformations, Transformation); bool atomicInit(); bool initProps(); TypeIndex type(); bool isCrtcSupported(int resIndex) const { return (m_possibleCrtcs & (1 << resIndex)); } QVector formats() const { return m_formats; } DrmBuffer *current() const { return m_current; } DrmBuffer *next() const { return m_next; } void setCurrent(DrmBuffer *b) { m_current = b; } void setNext(DrmBuffer *b); void setTransformation(Transformations t); Transformations transformation(); bool atomicPopulate(drmModeAtomicReq *req); void flipBuffer(); void flipBufferWithDelete(); Transformations supportedTransformations() const { return m_supportedTransformations; } private: DrmBuffer *m_current = nullptr; DrmBuffer *m_next = nullptr; // TODO: See weston drm_output_check_plane_format for future use of these member variables QVector m_formats; // Possible formats, which can be presented on this plane // TODO: when using overlay planes in the future: restrict possible screens / crtcs of planes uint32_t m_possibleCrtcs; Transformations m_supportedTransformations = Transformation::Rotate0; }; } Q_DECLARE_OPERATORS_FOR_FLAGS(KWin::DrmPlane::Transformations) #endif