diff --git a/.reviewboardrc b/.reviewboardrc deleted file mode 100644 index d1522c1..0000000 --- a/.reviewboardrc +++ /dev/null @@ -1,3 +0,0 @@ -REVIEWBOARD_URL = "https://git.reviewboard.kde.org" -REPOSITORY = "git://anongit.kde.org/kwayland" -TARGET_GROUPS = "" diff --git a/CMakeLists.txt b/CMakeLists.txt index d8ecbc5..ac126a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,94 +1,94 @@ cmake_minimum_required(VERSION 3.0) -set(KF5_VERSION "5.39.0") # handled by release scripts +set(KF5_VERSION "5.43.0") # handled by release scripts project(KWayland VERSION ${KF5_VERSION}) # ECM setup include(FeatureSummary) -find_package(ECM 5.39.0 NO_MODULE) +find_package(ECM 5.42.0 NO_MODULE) set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://projects.kde.org/projects/kdesupport/extra-cmake-modules") feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(FeatureSummary) include(GenerateExportHeader) include(CMakePackageConfigHelpers) include(ECMSetupVersion) include(ECMGenerateHeaders) include(CMakeFindFrameworks) include(ECMPoQmTools) include(ECMAddQch) option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF) add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)") ecm_setup_version(PROJECT VARIABLE_PREFIX KWAYLAND VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kwayland_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5WaylandConfigVersion.cmake" SOVERSION 5) # Dependencies set(REQUIRED_QT_VERSION 5.7.0) find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Concurrent Gui) -find_package(Wayland 1.7 COMPONENTS Client Server) +find_package(Wayland 1.13 COMPONENTS Client Server) set_package_properties(Wayland PROPERTIES TYPE REQUIRED ) find_package(WaylandScanner) find_package(EGL) set_package_properties(EGL PROPERTIES TYPE REQUIRED) include(KDEInstallDirs) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(CheckIncludeFile) check_include_file("linux/input.h" HAVE_LINUX_INPUT_H) configure_file(config-kwayland.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kwayland.h) include_directories(${CMAKE_CURRENT_BINARY_DIR}) # adjusting CMAKE_C_FLAGS to get wayland protocols to compile set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu90") # Subdirectories if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") ecm_install_po_files_as_qm(po) endif() add_subdirectory(src) add_subdirectory(autotests) add_subdirectory(tests) # create a Config.cmake and a ConfigVersion.cmake file and install them set(CMAKECONFIG_INSTALL_DIR "${CMAKECONFIG_INSTALL_PREFIX}/KF5Wayland") if (BUILD_QCH) ecm_install_qch_export( TARGETS KF5Wayland_QCH FILE KF5WaylandQchTargets.cmake DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) set(PACKAGE_INCLUDE_QCHTARGETS "include(\"\${CMAKE_CURRENT_LIST_DIR}/KF5WaylandQchTargets.cmake\")") endif() configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/KF5WaylandConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KF5WaylandConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KF5WaylandConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KF5WaylandConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(EXPORT KF5WaylandTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KF5WaylandTargets.cmake NAMESPACE KF5:: ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kwayland_version.h DESTINATION ${KF5_INCLUDE_INSTALL_DIR} COMPONENT Devel ) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/autotests/client/CMakeLists.txt b/autotests/client/CMakeLists.txt index f2bc804..62ad2e6 100644 --- a/autotests/client/CMakeLists.txt +++ b/autotests/client/CMakeLists.txt @@ -1,392 +1,431 @@ ######################################################## # Test WaylandConnectionThread ######################################################## set( testWaylandConnectionThread_SRCS test_wayland_connection_thread.cpp ) add_executable(testWaylandConnectionThread ${testWaylandConnectionThread_SRCS}) target_link_libraries( testWaylandConnectionThread Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client Wayland::Server) add_test(NAME kwayland-testWaylandConnectionThread COMMAND testWaylandConnectionThread) ecm_mark_as_test(testWaylandConnectionThread) ######################################################## # Test WaylandRegistry ######################################################## set( testWaylandRegistry_SRCS test_wayland_registry.cpp ) add_executable(testWaylandRegistry ${testWaylandRegistry_SRCS}) target_link_libraries( testWaylandRegistry Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client Wayland::Server) add_test(NAME kwayland-testWaylandRegistry COMMAND testWaylandRegistry) ecm_mark_as_test(testWaylandRegistry) ######################################################## # Test WaylandFullscreenShell ######################################################## if(Wayland_VERSION VERSION_GREATER "1.4.0") + find_program(WESTON_EXECUTABLE weston DOC "Path to the weston executable.") + if(WESTON_EXECUTABLE) set( testWaylandFullscreenShell_SRCS test_wayland_fullscreen_shell.cpp ) add_executable(testWaylandFullscreenShell ${testWaylandFullscreenShell_SRCS}) target_link_libraries( testWaylandFullscreenShell Qt5::Test KF5::WaylandClient Wayland::Client) add_test(NAME kwayland-testWaylandFullscreenShell COMMAND testWaylandFullscreenShell) ecm_mark_as_test(testWaylandFullscreenShell) + else() + message(STATUS "The weston executable was not found. Some autotests will not be executed.") + endif() endif() ######################################################## # Test WaylandOutput ######################################################## set( testWaylandOutput_SRCS test_wayland_output.cpp ) add_executable(testWaylandOutput ${testWaylandOutput_SRCS}) target_link_libraries( testWaylandOutput Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client Wayland::Server) add_test(NAME kwayland-testWaylandOutput COMMAND testWaylandOutput) ecm_mark_as_test(testWaylandOutput) ######################################################## # Test WaylandShell ######################################################## set( testWaylandShell_SRCS test_wayland_shell.cpp ) add_executable(testWaylandShell ${testWaylandShell_SRCS}) target_link_libraries( testWaylandShell Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client Wayland::Server) add_test(NAME kwayland-testWaylandShell COMMAND testWaylandShell) ecm_mark_as_test(testWaylandShell) ######################################################## # Test WaylandSurface ######################################################## set( testWaylandSurface_SRCS test_wayland_surface.cpp ) add_executable(testWaylandSurface ${testWaylandSurface_SRCS}) target_link_libraries( testWaylandSurface Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client Wayland::Server) add_test(NAME kwayland-testWaylandSurface COMMAND testWaylandSurface) ecm_mark_as_test(testWaylandSurface) ######################################################## # Test WaylandSeat ######################################################## if (HAVE_LINUX_INPUT_H) set( testWaylandSeat_SRCS test_wayland_seat.cpp ) add_executable(testWaylandSeat ${testWaylandSeat_SRCS}) target_link_libraries( testWaylandSeat Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client Wayland::Server) add_test(NAME kwayland-testWaylandSeat COMMAND testWaylandSeat) ecm_mark_as_test(testWaylandSeat) endif() ######################################################## # Test ShmPool ######################################################## set( testShmPool_SRCS test_shm_pool.cpp ) add_executable(testShmPool ${testShmPool_SRCS}) target_link_libraries( testShmPool Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer) add_test(NAME kwayland-testShmPool COMMAND testShmPool) ecm_mark_as_test(testShmPool) ######################################################## # Test KWin OutputManagement ######################################################## set( test_wayland_outputmanagement_SRCS test_wayland_outputmanagement.cpp ) add_executable(testWaylandOutputManagement ${test_wayland_outputmanagement_SRCS}) target_link_libraries( testWaylandOutputManagement Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client) add_test(NAME kwayland-testWaylandOutputManagement COMMAND testWaylandOutputManagement) ecm_mark_as_test(testWaylandOutputManagement) ######################################################## # Test KWin OutputDevice ######################################################## set( test_wayland_outputdevice_SRCS test_wayland_outputdevice.cpp ) add_executable(testWaylandOutputDevice ${test_wayland_outputdevice_SRCS}) target_link_libraries( testWaylandOutputDevice Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client) add_test(NAME kwayland-testWaylandOutputDevice COMMAND testWaylandOutputDevice) ecm_mark_as_test(testWaylandOutputDevice) ######################################################## # Test Compositor ######################################################## set( testCompositor_SRCS test_compositor.cpp ) add_executable(testCompositor ${testCompositor_SRCS}) target_link_libraries( testCompositor Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer) add_test(NAME kwayland-testCompositor COMMAND testCompositor) ecm_mark_as_test(testCompositor) ######################################################## # Test SubCompositor ######################################################## set( testSubCompositor_SRCS test_wayland_subcompositor.cpp ) add_executable(testSubCompositor ${testSubCompositor_SRCS}) target_link_libraries( testSubCompositor Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer) add_test(NAME kwayland-testSubCompositor COMMAND testSubCompositor) ecm_mark_as_test(testSubCompositor) ######################################################## # Test SubSurface ######################################################## set( testSubSurface_SRCS test_wayland_subsurface.cpp ) add_executable(testSubSurface ${testSubSurface_SRCS}) target_link_libraries( testSubSurface Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client) add_test(NAME kwayland-testSubSurface COMMAND testSubSurface) ecm_mark_as_test(testSubSurface) ######################################################## # Test Region ######################################################## set( testRegion_SRCS test_wayland_region.cpp ) add_executable(testRegion ${testRegion_SRCS}) target_link_libraries( testRegion Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer) add_test(NAME kwayland-testRegion COMMAND testRegion) ecm_mark_as_test(testRegion) ######################################################## # Test Blur ######################################################## set( testBlur_SRCS test_wayland_blur.cpp ) add_executable(testBlur ${testBlur_SRCS}) target_link_libraries( testBlur Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer) add_test(NAME kwayland-testBlur COMMAND testBlur) ecm_mark_as_test(testBlur) ######################################################## # Test Contrast ######################################################## set( testContrast_SRCS test_wayland_contrast.cpp ) add_executable(testContrast ${testContrast_SRCS}) target_link_libraries( testContrast Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client) add_test(NAME kwayland-testContrast COMMAND testContrast) ecm_mark_as_test(testContrast) ######################################################## # Test Slide ######################################################## set( testSlide_SRCS test_wayland_slide.cpp ) add_executable(testSlide ${testSlide_SRCS}) target_link_libraries( testSlide Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client) add_test(NAME kwayland-testSlide COMMAND testSlide) ecm_mark_as_test(testSlide) ######################################################## # Test Window Management ######################################################## set( testWindowmanagement_SRCS test_wayland_windowmanagement.cpp ) add_executable(testWindowmanagement ${testWindowmanagement_SRCS}) target_link_libraries( testWindowmanagement Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client) add_test(NAME kwayland-testWindowmanagement COMMAND testWindowmanagement) ecm_mark_as_test(testWindowmanagement) ######################################################## # Test DataSource ######################################################## set( testDataSource_SRCS test_datasource.cpp ) add_executable(testDataSource ${testDataSource_SRCS}) target_link_libraries( testDataSource Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client) add_test(NAME kwayland-testDataSource COMMAND testDataSource) ecm_mark_as_test(testDataSource) ######################################################## # Test DataDevice ######################################################## set( testDataDevice_SRCS test_datadevice.cpp ) add_executable(testDataDevice ${testDataDevice_SRCS}) target_link_libraries( testDataDevice Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client) add_test(NAME kwayland-testDataDevice COMMAND testDataDevice) ecm_mark_as_test(testDataDevice) ######################################################## # Test ServerSideDecoration ######################################################## set( testServerSideDecoration_SRCS test_server_side_decoration.cpp ) add_executable(testServerSideDecoration ${testServerSideDecoration_SRCS}) target_link_libraries( testServerSideDecoration Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client) add_test(NAME kwayland-testServerSideDecoration COMMAND testServerSideDecoration) ecm_mark_as_test(testServerSideDecoration) ######################################################## # Test Drag'N'Drop ######################################################## set( testDragAndDrop_SRCS test_drag_drop.cpp ) add_executable(testDragAndDrop ${testDragAndDrop_SRCS}) target_link_libraries( testDragAndDrop Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client) add_test(NAME kwayland-testDragAndDrop COMMAND testDragAndDrop) ecm_mark_as_test(testDragAndDrop) ######################################################## # Test PlasmaShell ######################################################## set( testPlasmaShell_SRCS test_plasmashell.cpp ) add_executable(testPlasmaShell ${testPlasmaShell_SRCS}) target_link_libraries( testPlasmaShell Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client) add_test(NAME kwayland-testPlasmaShell COMMAND testPlasmaShell) ecm_mark_as_test(testPlasmaShell) ######################################################## # Test Idle ######################################################## set( testIdle_SRCS test_idle.cpp ) add_executable(testIdle ${testIdle_SRCS}) target_link_libraries( testIdle Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer) add_test(NAME kwayland-testIdle COMMAND testIdle) ecm_mark_as_test(testIdle) ######################################################## # Test Shadow ######################################################## set( testShadow_SRCS test_shadow.cpp ) add_executable(testShadow ${testShadow_SRCS}) target_link_libraries( testShadow Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer) add_test(NAME kwayland-testShadow COMMAND testShadow) ecm_mark_as_test(testShadow) ######################################################## # Test FakeInput ######################################################## if (HAVE_LINUX_INPUT_H) set( testFakeInput_SRCS test_fake_input.cpp ) add_executable(testFakeInput ${testFakeInput_SRCS}) target_link_libraries( testFakeInput Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer) add_test(NAME kwayland-testFakeInput COMMAND testFakeInput) ecm_mark_as_test(testFakeInput) endif() ######################################################## # Test PlasmaWindowModel ######################################################## if (HAVE_LINUX_INPUT_H) set( testPlasmaWindowModel_SRCS test_plasma_window_model.cpp ) add_executable(testPlasmaWindowModel ${testPlasmaWindowModel_SRCS}) target_link_libraries( testPlasmaWindowModel Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer) add_test(NAME kwayland-testPlasmaWindowModel COMMAND testPlasmaWindowModel) ecm_mark_as_test(testPlasmaWindowModel) endif() ######################################################## # Test TextInput ######################################################## set( testTextInput_SRCS test_text_input.cpp ) add_executable(testTextInput ${testTextInput_SRCS}) target_link_libraries( testTextInput Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer) add_test(NAME kwayland-testTextInput COMMAND testTextInput) ecm_mark_as_test(testTextInput) ######################################################## # Test Error ######################################################## set( testError_SRCS test_error.cpp ) add_executable(testError ${testError_SRCS}) target_link_libraries( testError Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client) add_test(NAME kwayland-testError COMMAND testError) ecm_mark_as_test(testError) ######################################################## # Test Selection ######################################################## set( testSelection_SRCS test_selection.cpp ) add_executable(testSelection ${testSelection_SRCS}) target_link_libraries( testSelection Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client) add_test(NAME kwayland-testSelection COMMAND testSelection) ecm_mark_as_test(testSelection) ######################################################## # Test XdgShellV5 ######################################################## set( testXdgShellV5_SRCS test_xdg_shell.cpp test_xdg_shell_v5.cpp ) add_executable(testXdgShellV5 ${testXdgShellV5_SRCS}) target_link_libraries( testXdgShellV5 Qt5::Test Qt5::Gui KF5::WaylandServer KF5::WaylandClient Wayland::Client) add_test(NAME kwayland-testXdgShellV5 COMMAND testXdgShellV5) ecm_mark_as_test(testXdgShellV5) ######################################################## # Test XdgForeign ######################################################## set( testXdgForeign_SRCS test_xdg_foreign.cpp ) add_executable(testXdgForeign ${testXdgForeign_SRCS}) target_link_libraries( testXdgForeign Qt5::Test Qt5::Gui KF5::WaylandServer KF5::WaylandClient Wayland::Client) add_test(NAME kwayland-testXdgForeign COMMAND testXdgForeign) ecm_mark_as_test(testXdgForeign) ######################################################## # Test XdgShellV6 ######################################################## set( testXdgShellV6_SRCS test_xdg_shell.cpp test_xdg_shell_v6.cpp ) add_executable(testXdgShellV6 ${testXdgShellV6_SRCS}) target_link_libraries( testXdgShellV6 Qt5::Test Qt5::Gui KF5::WaylandServer KF5::WaylandClient Wayland::Client) -add_test(kwayland-testXdgShellV6 COMMAND testXdgShellV6) +add_test(NAME kwayland-testXdgShellV6 COMMAND testXdgShellV6) ecm_mark_as_test(testXdgShellV6) ######################################################## # Test Pointer Constraints ######################################################## add_executable(testPointerConstraints test_pointer_constraints.cpp) target_link_libraries( testPointerConstraints Qt5::Test Qt5::Gui KF5::WaylandServer KF5::WaylandClient Wayland::Client) add_test(NAME kwayland-testPointerConstraints COMMAND testPointerConstraints) ecm_mark_as_test(testPointerConstraints) + +######################################################## +# Test Filter +######################################################## +set( testFilter_SRCS + test_wayland_filter.cpp + ) +add_executable(testFilter ${testFilter_SRCS}) +target_link_libraries( testFilter Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Server) +add_test(NAME kwayland-testFilter COMMAND testFilter) +ecm_mark_as_test(testFilter) + +######################################################## +# Test Appmenu +######################################################## +set( testAppmenu_SRCS + test_wayland_appmenu.cpp + ) +add_executable(testAppmenu ${testAppmenu_SRCS}) +target_link_libraries( testAppmenu Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer) +add_test(NAME kwayland-testAppmenu COMMAND testAppmenu) +ecm_mark_as_test(testAppmenu) + +######################################################## +# Test Appmenu +######################################################## +set( testServerSideDecorationPalette_SRCS + test_server_side_decoration_palette.cpp + ) +add_executable(testServerSideDecorationPalette ${testServerSideDecorationPalette_SRCS}) +target_link_libraries( testServerSideDecorationPalette Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer) +add_test(NAME kwayland-testServerSideDecorationPalette COMMAND testServerSideDecorationPalette) +ecm_mark_as_test(testServerSideDecorationPalette) + ######################################################## # Test RemoteAccess ######################################################## set( testRemoteAccess_SRCS test_remote_access.cpp ) add_executable(testRemoteAccess ${testRemoteAccess_SRCS}) target_link_libraries( testRemoteAccess Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer) add_test(kwayland-testRemoteAccess testRemoteAccess) ecm_mark_as_test(testRemoteAccess) diff --git a/autotests/client/test_datadevice.cpp b/autotests/client/test_datadevice.cpp index bcdf1d9..eca1cdb 100644 --- a/autotests/client/test_datadevice.cpp +++ b/autotests/client/test_datadevice.cpp @@ -1,559 +1,560 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ // Qt #include // KWayland #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/datadevice.h" #include "../../src/client/datadevicemanager.h" #include "../../src/client/datasource.h" #include "../../src/client/compositor.h" #include "../../src/client/keyboard.h" #include "../../src/client/pointer.h" #include "../../src/client/registry.h" #include "../../src/client/seat.h" #include "../../src/client/surface.h" #include "../../src/server/display.h" #include "../../src/server/datadevicemanager_interface.h" #include "../../src/server/datasource_interface.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/pointer_interface.h" #include "../../src/server/seat_interface.h" #include "../../src/server/surface_interface.h" // Wayland #include class TestDataDevice : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testCreate(); void testDrag(); void testDragInternally(); void testSetSelection(); void testSendSelectionOnSeat(); void testReplaceSource(); void testDestroy(); private: KWayland::Server::Display *m_display = nullptr; KWayland::Server::DataDeviceManagerInterface *m_dataDeviceManagerInterface = nullptr; KWayland::Server::CompositorInterface *m_compositorInterface = nullptr; KWayland::Server::SeatInterface *m_seatInterface = nullptr; KWayland::Client::ConnectionThread *m_connection = nullptr; KWayland::Client::DataDeviceManager *m_dataDeviceManager = nullptr; KWayland::Client::Compositor *m_compositor = nullptr; KWayland::Client::Seat *m_seat = nullptr; KWayland::Client::EventQueue *m_queue = nullptr; QThread *m_thread = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-datadevice-0"); void TestDataDevice::init() { + qRegisterMetaType(); using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, SIGNAL(connected())); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); KWayland::Client::Registry registry; QSignalSpy dataDeviceManagerSpy(®istry, SIGNAL(dataDeviceManagerAnnounced(quint32,quint32))); QVERIFY(dataDeviceManagerSpy.isValid()); QSignalSpy seatSpy(®istry, SIGNAL(seatAnnounced(quint32,quint32))); QVERIFY(seatSpy.isValid()); QSignalSpy compositorSpy(®istry, SIGNAL(compositorAnnounced(quint32,quint32))); QVERIFY(compositorSpy.isValid()); QVERIFY(!registry.eventQueue()); registry.setEventQueue(m_queue); QCOMPARE(registry.eventQueue(), m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); m_dataDeviceManagerInterface = m_display->createDataDeviceManager(m_display); m_dataDeviceManagerInterface->create(); QVERIFY(m_dataDeviceManagerInterface->isValid()); QVERIFY(dataDeviceManagerSpy.wait()); m_dataDeviceManager = registry.createDataDeviceManager(dataDeviceManagerSpy.first().first().value(), dataDeviceManagerSpy.first().last().value(), this); m_seatInterface = m_display->createSeat(m_display); m_seatInterface->setHasPointer(true); m_seatInterface->create(); QVERIFY(m_seatInterface->isValid()); QVERIFY(seatSpy.wait()); m_seat = registry.createSeat(seatSpy.first().first().value(), seatSpy.first().last().value(), this); QVERIFY(m_seat->isValid()); QSignalSpy pointerChangedSpy(m_seat, SIGNAL(hasPointerChanged(bool))); QVERIFY(pointerChangedSpy.isValid()); QVERIFY(pointerChangedSpy.wait()); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); QVERIFY(compositorSpy.wait()); m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); QVERIFY(m_compositor->isValid()); } void TestDataDevice::cleanup() { if (m_dataDeviceManager) { delete m_dataDeviceManager; m_dataDeviceManager = nullptr; } if (m_seat) { delete m_seat; m_seat = nullptr; } if (m_compositor) { delete m_compositor; m_compositor = nullptr; } if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_display; m_display = nullptr; } void TestDataDevice::testCreate() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataDeviceCreated(KWayland::Server::DataDeviceInterface*))); QVERIFY(dataDeviceCreatedSpy.isValid()); QScopedPointer dataDevice(m_dataDeviceManager->getDataDevice(m_seat)); QVERIFY(dataDevice->isValid()); QVERIFY(dataDeviceCreatedSpy.wait()); QCOMPARE(dataDeviceCreatedSpy.count(), 1); auto deviceInterface = dataDeviceCreatedSpy.first().first().value(); QVERIFY(deviceInterface); QCOMPARE(deviceInterface->seat(), m_seatInterface); QVERIFY(!deviceInterface->dragSource()); QVERIFY(!deviceInterface->origin()); QVERIFY(!deviceInterface->icon()); QVERIFY(!deviceInterface->selection()); QVERIFY(deviceInterface->parentResource()); QVERIFY(!m_seatInterface->selection()); m_seatInterface->setSelection(deviceInterface); QCOMPARE(m_seatInterface->selection(), deviceInterface); // and destroy QSignalSpy destroyedSpy(deviceInterface, &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); dataDevice.reset(); QVERIFY(destroyedSpy.wait()); QVERIFY(!m_seatInterface->selection()); } void TestDataDevice::testDrag() { using namespace KWayland::Client; using namespace KWayland::Server; QScopedPointer pointer(m_seat->createPointer()); QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataDeviceCreated(KWayland::Server::DataDeviceInterface*))); QVERIFY(dataDeviceCreatedSpy.isValid()); QScopedPointer dataDevice(m_dataDeviceManager->getDataDevice(m_seat)); QVERIFY(dataDevice->isValid()); QVERIFY(dataDeviceCreatedSpy.wait()); QCOMPARE(dataDeviceCreatedSpy.count(), 1); auto deviceInterface = dataDeviceCreatedSpy.first().first().value(); QVERIFY(deviceInterface); QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataSourceCreated(KWayland::Server::DataSourceInterface*))); QVERIFY(dataDeviceCreatedSpy.isValid()); QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource->isValid()); QVERIFY(dataSourceCreatedSpy.wait()); QCOMPARE(dataSourceCreatedSpy.count(), 1); auto sourceInterface = dataSourceCreatedSpy.first().first().value(); QVERIFY(sourceInterface); QSignalSpy surfaceCreatedSpy(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surface->isValid()); QVERIFY(surfaceCreatedSpy.wait()); QCOMPARE(surfaceCreatedSpy.count(), 1); auto surfaceInterface = surfaceCreatedSpy.first().first().value(); // now we have all we need to start a drag operation QSignalSpy dragStartedSpy(deviceInterface, SIGNAL(dragStarted())); QVERIFY(dragStartedSpy.isValid()); // first we need to fake the pointer enter m_seatInterface->setFocusedPointerSurface(surfaceInterface); m_seatInterface->pointerButtonPressed(1); QCoreApplication::processEvents(); dataDevice->startDrag(1, dataSource.data(), surface.data()); QVERIFY(dragStartedSpy.wait()); QCOMPARE(dragStartedSpy.count(), 1); QCOMPARE(deviceInterface->dragSource(), sourceInterface); QCOMPARE(deviceInterface->origin(), surfaceInterface); QVERIFY(!deviceInterface->icon()); } void TestDataDevice::testDragInternally() { using namespace KWayland::Client; using namespace KWayland::Server; QScopedPointer pointer(m_seat->createPointer()); QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataDeviceCreated(KWayland::Server::DataDeviceInterface*))); QVERIFY(dataDeviceCreatedSpy.isValid()); QScopedPointer dataDevice(m_dataDeviceManager->getDataDevice(m_seat)); QVERIFY(dataDevice->isValid()); QVERIFY(dataDeviceCreatedSpy.wait()); QCOMPARE(dataDeviceCreatedSpy.count(), 1); auto deviceInterface = dataDeviceCreatedSpy.first().first().value(); QVERIFY(deviceInterface); QSignalSpy surfaceCreatedSpy(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surface->isValid()); QVERIFY(surfaceCreatedSpy.wait()); QCOMPARE(surfaceCreatedSpy.count(), 1); auto surfaceInterface = surfaceCreatedSpy.first().first().value(); QScopedPointer iconSurface(m_compositor->createSurface()); QVERIFY(iconSurface->isValid()); QVERIFY(surfaceCreatedSpy.wait()); QCOMPARE(surfaceCreatedSpy.count(), 2); auto iconSurfaceInterface = surfaceCreatedSpy.last().first().value(); // now we have all we need to start a drag operation QSignalSpy dragStartedSpy(deviceInterface, SIGNAL(dragStarted())); QVERIFY(dragStartedSpy.isValid()); // first we need to fake the pointer enter m_seatInterface->setFocusedPointerSurface(surfaceInterface); m_seatInterface->pointerButtonPressed(1); QCoreApplication::processEvents(); dataDevice->startDragInternally(1, surface.data(), iconSurface.data()); QVERIFY(dragStartedSpy.wait()); QCOMPARE(dragStartedSpy.count(), 1); QVERIFY(!deviceInterface->dragSource()); QCOMPARE(deviceInterface->origin(), surfaceInterface); QCOMPARE(deviceInterface->icon(), iconSurfaceInterface); } void TestDataDevice::testSetSelection() { using namespace KWayland::Client; using namespace KWayland::Server; QScopedPointer pointer(m_seat->createPointer()); QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataDeviceCreated(KWayland::Server::DataDeviceInterface*))); QVERIFY(dataDeviceCreatedSpy.isValid()); QScopedPointer dataDevice(m_dataDeviceManager->getDataDevice(m_seat)); QVERIFY(dataDevice->isValid()); QVERIFY(dataDeviceCreatedSpy.wait()); QCOMPARE(dataDeviceCreatedSpy.count(), 1); auto deviceInterface = dataDeviceCreatedSpy.first().first().value(); QVERIFY(deviceInterface); QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataSourceCreated(KWayland::Server::DataSourceInterface*))); QVERIFY(dataDeviceCreatedSpy.isValid()); QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource->isValid()); dataSource->offer(QStringLiteral("text/plain")); QVERIFY(dataSourceCreatedSpy.wait()); QCOMPARE(dataSourceCreatedSpy.count(), 1); auto sourceInterface = dataSourceCreatedSpy.first().first().value(); QVERIFY(sourceInterface); // everything setup, now we can test setting the selection QSignalSpy selectionChangedSpy(deviceInterface, SIGNAL(selectionChanged(KWayland::Server::DataSourceInterface*))); QVERIFY(selectionChangedSpy.isValid()); QSignalSpy selectionClearedSpy(deviceInterface, SIGNAL(selectionCleared())); QVERIFY(selectionClearedSpy.isValid()); QVERIFY(!deviceInterface->selection()); dataDevice->setSelection(1, dataSource.data()); QVERIFY(selectionChangedSpy.wait()); QCOMPARE(selectionChangedSpy.count(), 1); QCOMPARE(selectionClearedSpy.count(), 0); QCOMPARE(selectionChangedSpy.first().first().value(), sourceInterface); QCOMPARE(deviceInterface->selection(), sourceInterface); // send selection to datadevice QSignalSpy selectionOfferedSpy(dataDevice.data(), SIGNAL(selectionOffered(KWayland::Client::DataOffer*))); QVERIFY(selectionOfferedSpy.isValid()); deviceInterface->sendSelection(deviceInterface); QVERIFY(selectionOfferedSpy.wait()); QCOMPARE(selectionOfferedSpy.count(), 1); auto dataOffer = selectionOfferedSpy.first().first().value(); QVERIFY(dataOffer); QCOMPARE(dataOffer->offeredMimeTypes().count(), 1); QCOMPARE(dataOffer->offeredMimeTypes().first().name(), QStringLiteral("text/plain")); // sending a new mimetype to the selection, should be announced in the offer QSignalSpy mimeTypeAddedSpy(dataOffer, SIGNAL(mimeTypeOffered(QString))); QVERIFY(mimeTypeAddedSpy.isValid()); dataSource->offer(QStringLiteral("text/html")); QVERIFY(mimeTypeAddedSpy.wait()); QCOMPARE(mimeTypeAddedSpy.count(), 1); QCOMPARE(mimeTypeAddedSpy.first().first().toString(), QStringLiteral("text/html")); QCOMPARE(dataOffer->offeredMimeTypes().count(), 2); QCOMPARE(dataOffer->offeredMimeTypes().first().name(), QStringLiteral("text/plain")); QCOMPARE(dataOffer->offeredMimeTypes().last().name(), QStringLiteral("text/html")); // now clear the selection dataDevice->clearSelection(1); QVERIFY(selectionClearedSpy.wait()); QCOMPARE(selectionChangedSpy.count(), 1); QCOMPARE(selectionClearedSpy.count(), 1); QVERIFY(!deviceInterface->selection()); // set another selection dataDevice->setSelection(2, dataSource.data()); QVERIFY(selectionChangedSpy.wait()); // now unbind the dataDevice QSignalSpy unboundSpy(deviceInterface, &DataDeviceInterface::unbound); QVERIFY(unboundSpy.isValid()); dataDevice.reset(); QVERIFY(unboundSpy.wait()); // send a selection to the unbound data device deviceInterface->sendSelection(deviceInterface); } void TestDataDevice::testSendSelectionOnSeat() { // this test verifies that the selection is sent when setting a focused keyboard using namespace KWayland::Client; using namespace KWayland::Server; // first add keyboard support to Seat QSignalSpy keyboardChangedSpy(m_seat, &Seat::hasKeyboardChanged); QVERIFY(keyboardChangedSpy.isValid()); m_seatInterface->setHasKeyboard(true); QVERIFY(keyboardChangedSpy.wait()); // now create DataDevice, Keyboard and a Surface QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &DataDeviceManagerInterface::dataDeviceCreated); QVERIFY(dataDeviceCreatedSpy.isValid()); QScopedPointer dataDevice(m_dataDeviceManager->getDataDevice(m_seat)); QVERIFY(dataDevice->isValid()); QVERIFY(dataDeviceCreatedSpy.wait()); auto serverDataDevice = dataDeviceCreatedSpy.first().first().value(); QVERIFY(serverDataDevice); QScopedPointer keyboard(m_seat->createKeyboard()); QVERIFY(keyboard->isValid()); QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surface->isValid()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); m_seatInterface->setFocusedKeyboardSurface(serverSurface); // now set the selection QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource->isValid()); dataSource->offer(QStringLiteral("text/plain")); dataDevice->setSelection(1, dataSource.data()); // we should get a selection offered for that on the data device QSignalSpy selectionOfferedSpy(dataDevice.data(), &DataDevice::selectionOffered); QVERIFY(selectionOfferedSpy.isValid()); QVERIFY(selectionOfferedSpy.wait()); QCOMPARE(selectionOfferedSpy.count(), 1); // now unfocus the keyboard m_seatInterface->setFocusedKeyboardSurface(nullptr); // if setting the same surface again, we should get another offer m_seatInterface->setFocusedKeyboardSurface(serverSurface); QVERIFY(selectionOfferedSpy.wait()); QCOMPARE(selectionOfferedSpy.count(), 2); // now let's try to destroy the data device and set a focused keyboard just while the data device is being destroyedd m_seatInterface->setFocusedKeyboardSurface(nullptr); QSignalSpy unboundSpy(serverDataDevice, &Resource::unbound); QVERIFY(unboundSpy.isValid()); dataDevice.reset(); QVERIFY(unboundSpy.wait()); m_seatInterface->setFocusedKeyboardSurface(serverSurface); } void TestDataDevice::testReplaceSource() { // this test verifies that replacing a data source cancels the previous source using namespace KWayland::Client; using namespace KWayland::Server; // first add keyboard support to Seat QSignalSpy keyboardChangedSpy(m_seat, &Seat::hasKeyboardChanged); QVERIFY(keyboardChangedSpy.isValid()); m_seatInterface->setHasKeyboard(true); QVERIFY(keyboardChangedSpy.wait()); // now create DataDevice, Keyboard and a Surface QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &DataDeviceManagerInterface::dataDeviceCreated); QVERIFY(dataDeviceCreatedSpy.isValid()); QScopedPointer dataDevice(m_dataDeviceManager->getDataDevice(m_seat)); QVERIFY(dataDevice->isValid()); QVERIFY(dataDeviceCreatedSpy.wait()); auto serverDataDevice = dataDeviceCreatedSpy.first().first().value(); QVERIFY(serverDataDevice); QScopedPointer keyboard(m_seat->createKeyboard()); QVERIFY(keyboard->isValid()); QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surface->isValid()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); m_seatInterface->setFocusedKeyboardSurface(serverSurface); // now set the selection QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource->isValid()); dataSource->offer(QStringLiteral("text/plain")); dataDevice->setSelection(1, dataSource.data()); QSignalSpy sourceCancelledSpy(dataSource.data(), &DataSource::cancelled); QVERIFY(sourceCancelledSpy.isValid()); // we should get a selection offered for that on the data device QSignalSpy selectionOfferedSpy(dataDevice.data(), &DataDevice::selectionOffered); QVERIFY(selectionOfferedSpy.isValid()); QVERIFY(selectionOfferedSpy.wait()); QCOMPARE(selectionOfferedSpy.count(), 1); // create a second data source and replace previous one QScopedPointer dataSource2(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource2->isValid()); dataSource2->offer(QStringLiteral("text/plain")); QSignalSpy sourceCancelled2Spy(dataSource2.data(), &DataSource::cancelled); QVERIFY(sourceCancelled2Spy.isValid()); dataDevice->setSelection(1, dataSource2.data()); QCOMPARE(selectionOfferedSpy.count(), 1); QVERIFY(sourceCancelledSpy.wait()); QCOMPARE(selectionOfferedSpy.count(), 2); QVERIFY(sourceCancelled2Spy.isEmpty()); // create a new DataDevice and replace previous one QScopedPointer dataDevice2(m_dataDeviceManager->getDataDevice(m_seat)); QVERIFY(dataDevice2->isValid()); QScopedPointer dataSource3(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource3->isValid()); dataSource3->offer(QStringLiteral("text/plain")); dataDevice2->setSelection(1, dataSource3.data()); QVERIFY(sourceCancelled2Spy.wait()); // try to crash by first destroying dataSource3 and setting a new DataSource QScopedPointer dataSource4(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource4->isValid()); dataSource4->offer(QStringLiteral("text/plain")); dataSource3.reset(); dataDevice2->setSelection(1, dataSource4.data()); QVERIFY(selectionOfferedSpy.wait()); } void TestDataDevice::testDestroy() { using namespace KWayland::Client; QScopedPointer dataDevice(m_dataDeviceManager->getDataDevice(m_seat)); QVERIFY(dataDevice->isValid()); connect(m_connection, &ConnectionThread::connectionDied, m_dataDeviceManager, &DataDeviceManager::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_seat, &Seat::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_compositor, &Compositor::destroy); connect(m_connection, &ConnectionThread::connectionDied, dataDevice.data(), &DataDevice::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_queue, &EventQueue::destroy); QSignalSpy connectionDiedSpy(m_connection, SIGNAL(connectionDied())); QVERIFY(connectionDiedSpy.isValid()); delete m_display; m_display = nullptr; QVERIFY(connectionDiedSpy.wait()); // now the data device should be destroyed; QVERIFY(!dataDevice->isValid()); // calling destroy again should not fail dataDevice->destroy(); } QTEST_GUILESS_MAIN(TestDataDevice) #include "test_datadevice.moc" diff --git a/autotests/client/test_datasource.cpp b/autotests/client/test_datasource.cpp index ab4c9c6..bd8b081 100644 --- a/autotests/client/test_datasource.cpp +++ b/autotests/client/test_datasource.cpp @@ -1,338 +1,339 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ // Qt #include #include // KWayland #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/datadevicemanager.h" #include "../../src/client/datasource.h" #include "../../src/client/registry.h" #include "../../src/server/display.h" #include "../../src/server/datadevicemanager_interface.h" #include "../../src/server/datasource_interface.h" // Wayland #include class TestDataSource : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testOffer(); void testTargetAccepts_data(); void testTargetAccepts(); void testRequestSend(); void testRequestSendOnUnbound(); void testCancel(); void testServerGet(); void testDestroy(); private: KWayland::Server::Display *m_display = nullptr; KWayland::Server::DataDeviceManagerInterface *m_dataDeviceManagerInterface = nullptr; KWayland::Client::ConnectionThread *m_connection = nullptr; KWayland::Client::DataDeviceManager *m_dataDeviceManager = nullptr; KWayland::Client::EventQueue *m_queue = nullptr; QThread *m_thread = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-datasource-0"); void TestDataSource::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, SIGNAL(connected())); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); KWayland::Client::Registry registry; QSignalSpy dataDeviceManagerSpy(®istry, SIGNAL(dataDeviceManagerAnnounced(quint32,quint32))); QVERIFY(dataDeviceManagerSpy.isValid()); QVERIFY(!registry.eventQueue()); registry.setEventQueue(m_queue); QCOMPARE(registry.eventQueue(), m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); m_dataDeviceManagerInterface = m_display->createDataDeviceManager(m_display); m_dataDeviceManagerInterface->create(); QVERIFY(m_dataDeviceManagerInterface->isValid()); QVERIFY(dataDeviceManagerSpy.wait()); m_dataDeviceManager = registry.createDataDeviceManager(dataDeviceManagerSpy.first().first().value(), dataDeviceManagerSpy.first().last().value(), this); } void TestDataSource::cleanup() { if (m_dataDeviceManager) { delete m_dataDeviceManager; m_dataDeviceManager = nullptr; } if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_display; m_display = nullptr; } void TestDataSource::testOffer() { using namespace KWayland::Client; using namespace KWayland::Server; + qRegisterMetaType(); QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataSourceCreated(KWayland::Server::DataSourceInterface*))); QVERIFY(dataSourceCreatedSpy.isValid()); QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource->isValid()); QVERIFY(dataSourceCreatedSpy.wait()); QCOMPARE(dataSourceCreatedSpy.count(), 1); QPointer serverDataSource = dataSourceCreatedSpy.first().first().value(); QVERIFY(!serverDataSource.isNull()); QCOMPARE(serverDataSource->mimeTypes().count(), 0); QVERIFY(serverDataSource->parentResource()); QSignalSpy offeredSpy(serverDataSource.data(), SIGNAL(mimeTypeOffered(QString))); QVERIFY(offeredSpy.isValid()); const QString plain = QStringLiteral("text/plain"); QMimeDatabase db; dataSource->offer(db.mimeTypeForName(plain)); QVERIFY(offeredSpy.wait()); QCOMPARE(offeredSpy.count(), 1); QCOMPARE(offeredSpy.last().first().toString(), plain); QCOMPARE(serverDataSource->mimeTypes().count(), 1); QCOMPARE(serverDataSource->mimeTypes().first(), plain); const QString html = QStringLiteral("text/html"); dataSource->offer(db.mimeTypeForName(html)); QVERIFY(offeredSpy.wait()); QCOMPARE(offeredSpy.count(), 2); QCOMPARE(offeredSpy.first().first().toString(), plain); QCOMPARE(offeredSpy.last().first().toString(), html); QCOMPARE(serverDataSource->mimeTypes().count(), 2); QCOMPARE(serverDataSource->mimeTypes().first(), plain); QCOMPARE(serverDataSource->mimeTypes().last(), html); // try destroying the client side, should trigger a destroy of server side dataSource.reset(); QVERIFY(!serverDataSource.isNull()); wl_display_flush(m_connection->display()); // after running the event loop the Wayland event should be delivered, but it uses delete later QCoreApplication::processEvents(); QVERIFY(!serverDataSource.isNull()); // so once more event loop QCoreApplication::processEvents(); QVERIFY(serverDataSource.isNull()); } void TestDataSource::testTargetAccepts_data() { QTest::addColumn("mimeType"); QTest::newRow("empty") << QString(); QTest::newRow("text/plain") << QStringLiteral("text/plain"); } void TestDataSource::testTargetAccepts() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataSourceCreated(KWayland::Server::DataSourceInterface*))); QVERIFY(dataSourceCreatedSpy.isValid()); QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource->isValid()); QSignalSpy targetAcceptsSpy(dataSource.data(), SIGNAL(targetAccepts(QString))); QVERIFY(targetAcceptsSpy.isValid()); QVERIFY(dataSourceCreatedSpy.wait()); QCOMPARE(dataSourceCreatedSpy.count(), 1); QFETCH(QString, mimeType); dataSourceCreatedSpy.first().first().value()->accept(mimeType); QVERIFY(targetAcceptsSpy.wait()); QCOMPARE(targetAcceptsSpy.count(), 1); QCOMPARE(targetAcceptsSpy.first().first().toString(), mimeType); } void TestDataSource::testRequestSend() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataSourceCreated(KWayland::Server::DataSourceInterface*))); QVERIFY(dataSourceCreatedSpy.isValid()); QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource->isValid()); QSignalSpy sendRequestedSpy(dataSource.data(), SIGNAL(sendDataRequested(QString,qint32))); QVERIFY(sendRequestedSpy.isValid()); const QString plain = QStringLiteral("text/plain"); QVERIFY(dataSourceCreatedSpy.wait()); QCOMPARE(dataSourceCreatedSpy.count(), 1); QTemporaryFile file; QVERIFY(file.open()); dataSourceCreatedSpy.first().first().value()->requestData(plain, file.handle()); QVERIFY(sendRequestedSpy.wait()); QCOMPARE(sendRequestedSpy.count(), 1); QCOMPARE(sendRequestedSpy.first().first().toString(), plain); QCOMPARE(sendRequestedSpy.first().last().value(), file.handle()); QVERIFY(sendRequestedSpy.first().last().value() != -1); QFile writeFile; QVERIFY(writeFile.open(sendRequestedSpy.first().last().value(), QFile::WriteOnly, QFileDevice::AutoCloseHandle)); writeFile.close(); } void TestDataSource::testRequestSendOnUnbound() { // this test verifies that the server doesn't crash when requesting a send on an unbound DataSource using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, &DataDeviceManagerInterface::dataSourceCreated); QVERIFY(dataSourceCreatedSpy.isValid()); QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource->isValid()); QVERIFY(dataSourceCreatedSpy.wait()); QCOMPARE(dataSourceCreatedSpy.count(), 1); auto sds = dataSourceCreatedSpy.first().first().value(); QVERIFY(sds); QSignalSpy unboundSpy(sds, &Resource::unbound); QVERIFY(unboundSpy.isValid()); dataSource.reset(); QVERIFY(unboundSpy.wait()); sds->requestData(QStringLiteral("text/plain"), -1); } void TestDataSource::testCancel() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataSourceCreated(KWayland::Server::DataSourceInterface*))); QVERIFY(dataSourceCreatedSpy.isValid()); QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource->isValid()); QSignalSpy cancelledSpy(dataSource.data(), SIGNAL(cancelled())); QVERIFY(cancelledSpy.isValid()); QVERIFY(dataSourceCreatedSpy.wait()); QCOMPARE(cancelledSpy.count(), 0); dataSourceCreatedSpy.first().first().value()->cancel(); QVERIFY(cancelledSpy.wait()); QCOMPARE(cancelledSpy.count(), 1); } void TestDataSource::testServerGet() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataSourceCreated(KWayland::Server::DataSourceInterface*))); QVERIFY(dataSourceCreatedSpy.isValid()); QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource->isValid()); QVERIFY(!DataSourceInterface::get(nullptr)); QVERIFY(dataSourceCreatedSpy.wait()); auto d = dataSourceCreatedSpy.first().first().value(); QCOMPARE(DataSourceInterface::get(d->resource()), d); QVERIFY(!DataSourceInterface::get(nullptr)); } void TestDataSource::testDestroy() { using namespace KWayland::Client; QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource->isValid()); connect(m_connection, &ConnectionThread::connectionDied, m_dataDeviceManager, &DataDeviceManager::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_queue, &EventQueue::destroy); connect(m_connection, &ConnectionThread::connectionDied, dataSource.data(), &DataSource::destroy); QSignalSpy connectionDiedSpy(m_connection, SIGNAL(connectionDied())); QVERIFY(connectionDiedSpy.isValid()); delete m_display; m_display = nullptr; QVERIFY(connectionDiedSpy.wait()); // now the pool should be destroyed; QVERIFY(!dataSource->isValid()); // calling destroy again should not fail dataSource->destroy(); } QTEST_GUILESS_MAIN(TestDataSource) #include "test_datasource.moc" diff --git a/autotests/client/test_drag_drop.cpp b/autotests/client/test_drag_drop.cpp index afbb2d5..a8d6221 100644 --- a/autotests/client/test_drag_drop.cpp +++ b/autotests/client/test_drag_drop.cpp @@ -1,362 +1,385 @@ /******************************************************************** Copyright 2016 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ // Qt #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/datadevice.h" #include "../../src/client/datadevicemanager.h" #include "../../src/client/datasource.h" #include "../../src/client/event_queue.h" #include "../../src/client/pointer.h" #include "../../src/client/registry.h" #include "../../src/client/seat.h" #include "../../src/client/shell.h" #include "../../src/client/shm_pool.h" #include "../../src/client/surface.h" #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/datadevicemanager_interface.h" #include "../../src/server/seat_interface.h" #include "../../src/server/shell_interface.h" class TestDragAndDrop : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testDragAndDrop(); void testPointerEventsIgnored(); private: KWayland::Client::Surface *createSurface(); KWayland::Server::SurfaceInterface *getServerSurface(); KWayland::Server::Display *m_display = nullptr; KWayland::Server::CompositorInterface *m_compositorInterface = nullptr; KWayland::Server::DataDeviceManagerInterface *m_dataDeviceManagerInterface = nullptr; KWayland::Server::SeatInterface *m_seatInterface = nullptr; KWayland::Server::ShellInterface *m_shellInterface = nullptr; KWayland::Client::ConnectionThread *m_connection = nullptr; KWayland::Client::Compositor *m_compositor = nullptr; KWayland::Client::EventQueue *m_queue = nullptr; KWayland::Client::DataDevice *m_dataDevice = nullptr; KWayland::Client::DataSource *m_dataSource = nullptr; QThread *m_thread = nullptr; KWayland::Client::Registry *m_registry = nullptr; KWayland::Client::Seat *m_seat = nullptr; KWayland::Client::Pointer *m_pointer = nullptr; KWayland::Client::DataDeviceManager *m_ddm = nullptr; KWayland::Client::ShmPool *m_shm = nullptr; KWayland::Client::Shell *m_shell = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-drag-n-drop-0"); void TestDragAndDrop::init() { using namespace KWayland::Server; using namespace KWayland::Client; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); m_seatInterface = m_display->createSeat(m_display); m_seatInterface->setHasPointer(true); m_seatInterface->create(); QVERIFY(m_seatInterface->isValid()); m_dataDeviceManagerInterface = m_display->createDataDeviceManager(m_display); m_dataDeviceManagerInterface->create(); QVERIFY(m_dataDeviceManagerInterface->isValid()); m_display->createShm(); m_shellInterface = m_display->createShell(m_display); m_shellInterface->create(); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); m_registry = new Registry(); QSignalSpy interfacesAnnouncedSpy(m_registry, &Registry::interfaceAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QVERIFY(!m_registry->eventQueue()); m_registry->setEventQueue(m_queue); QCOMPARE(m_registry->eventQueue(), m_queue); m_registry->create(m_connection); QVERIFY(m_registry->isValid()); m_registry->setup(); QVERIFY(interfacesAnnouncedSpy.wait()); #define CREATE(variable, factory, iface) \ variable = m_registry->create##factory(m_registry->interface(Registry::Interface::iface).name, m_registry->interface(Registry::Interface::iface).version, this); \ QVERIFY(variable); CREATE(m_compositor, Compositor, Compositor) CREATE(m_seat, Seat, Seat) CREATE(m_ddm, DataDeviceManager, DataDeviceManager) CREATE(m_shm, ShmPool, Shm) CREATE(m_shell, Shell, Shell) #undef CREATE QSignalSpy pointerSpy(m_seat, &Seat::hasPointerChanged); QVERIFY(pointerSpy.isValid()); QVERIFY(pointerSpy.wait()); m_pointer = m_seat->createPointer(m_seat); QVERIFY(m_pointer->isValid()); m_dataDevice = m_ddm->getDataDevice(m_seat, this); QVERIFY(m_dataDevice->isValid()); m_dataSource = m_ddm->createDataSource(this); QVERIFY(m_dataSource->isValid()); m_dataSource->offer(QStringLiteral("text/plain")); } void TestDragAndDrop::cleanup() { #define DELETE(name) \ if (name) { \ delete name; \ name = nullptr; \ } DELETE(m_dataSource) DELETE(m_dataDevice) DELETE(m_shell) DELETE(m_shm) DELETE(m_compositor) DELETE(m_ddm) DELETE(m_seat) DELETE(m_queue) DELETE(m_registry) #undef DELETE if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_display; m_display = nullptr; } KWayland::Client::Surface *TestDragAndDrop::createSurface() { using namespace KWayland::Client; auto s = m_compositor->createSurface(); QImage img(QSize(100, 200), QImage::Format_RGB32); img.fill(Qt::red); s->attachBuffer(m_shm->createBuffer(img)); s->damage(QRect(0, 0, 100, 200)); s->commit(Surface::CommitFlag::None); return s; } KWayland::Server::SurfaceInterface *TestDragAndDrop::getServerSurface() { using namespace KWayland::Server; QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); if (!surfaceCreatedSpy.isValid()) { return nullptr; } if (!surfaceCreatedSpy.wait()) { return nullptr; } return surfaceCreatedSpy.first().first().value(); } void TestDragAndDrop::testDragAndDrop() { // this test verifies the very basic drag and drop on one surface, an enter, a move and the drop using namespace KWayland::Server; using namespace KWayland::Client; // first create a window QScopedPointer s(createSurface()); auto serverSurface = getServerSurface(); QVERIFY(serverSurface); + QSignalSpy dataSourceSelectedActionChangedSpy(m_dataSource, &DataSource::selectedDragAndDropActionChanged); + QVERIFY(dataSourceSelectedActionChangedSpy.isValid()); + // now we need to pass pointer focus to the Surface and simulate a button press QSignalSpy buttonPressSpy(m_pointer, &Pointer::buttonStateChanged); QVERIFY(buttonPressSpy.isValid()); m_seatInterface->setFocusedPointerSurface(serverSurface); m_seatInterface->setTimestamp(2); m_seatInterface->pointerButtonPressed(1); QVERIFY(buttonPressSpy.wait()); QCOMPARE(buttonPressSpy.first().at(1).value(), quint32(2)); // add some signal spies for client side QSignalSpy dragEnteredSpy(m_dataDevice, &DataDevice::dragEntered); QVERIFY(dragEnteredSpy.isValid()); QSignalSpy dragMotionSpy(m_dataDevice, &DataDevice::dragMotion); QVERIFY(dragMotionSpy.isValid()); QSignalSpy pointerMotionSpy(m_pointer, &Pointer::motion); QVERIFY(pointerMotionSpy.isValid()); + QSignalSpy sourceDropSpy(m_dataSource, &DataSource::dragAndDropPerformed); + QVERIFY(sourceDropSpy.isValid()); // now we can start the drag and drop QSignalSpy dragStartedSpy(m_seatInterface, &SeatInterface::dragStarted); QVERIFY(dragStartedSpy.isValid()); + m_dataSource->setDragAndDropActions(DataDeviceManager::DnDAction::Copy | DataDeviceManager::DnDAction::Move); m_dataDevice->startDrag(buttonPressSpy.first().first().value(), m_dataSource, s.data()); QVERIFY(dragStartedSpy.wait()); QCOMPARE(m_seatInterface->dragSurface(), serverSurface); QCOMPARE(m_seatInterface->dragSurfaceTransformation(), QMatrix4x4()); QVERIFY(!m_seatInterface->dragSource()->icon()); QCOMPARE(m_seatInterface->dragSource()->dragImplicitGrabSerial(), buttonPressSpy.first().first().value()); QVERIFY(dragEnteredSpy.wait()); QCOMPARE(dragEnteredSpy.count(), 1); QCOMPARE(dragEnteredSpy.first().first().value(), m_display->serial()); QCOMPARE(dragEnteredSpy.first().last().toPointF(), QPointF(0, 0)); QCOMPARE(m_dataDevice->dragSurface().data(), s.data()); + auto offer = m_dataDevice->dragOffer(); + QVERIFY(offer); + QCOMPARE(offer->selectedDragAndDropAction(), DataDeviceManager::DnDAction::None); + QSignalSpy offerActionChangedSpy(offer, &DataOffer::selectedDragAndDropActionChanged); + QVERIFY(offerActionChangedSpy.isValid()); QCOMPARE(m_dataDevice->dragOffer()->offeredMimeTypes().count(), 1); QCOMPARE(m_dataDevice->dragOffer()->offeredMimeTypes().first().name(), QStringLiteral("text/plain")); + QTRY_COMPARE(offer->sourceDragAndDropActions(), DataDeviceManager::DnDAction::Copy | DataDeviceManager::DnDAction::Move); + offer->setDragAndDropActions(DataDeviceManager::DnDAction::Copy | DataDeviceManager::DnDAction::Move, DataDeviceManager::DnDAction::Move); + QVERIFY(offerActionChangedSpy.wait()); + QCOMPARE(offerActionChangedSpy.count(), 1); + QCOMPARE(offer->selectedDragAndDropAction(), DataDeviceManager::DnDAction::Move); + QCOMPARE(dataSourceSelectedActionChangedSpy.count(), 1); + QCOMPARE(m_dataSource->selectedDragAndDropAction(), DataDeviceManager::DnDAction::Move); // simulate motion m_seatInterface->setTimestamp(3); m_seatInterface->setPointerPos(QPointF(3, 3)); QVERIFY(dragMotionSpy.wait()); QCOMPARE(dragMotionSpy.count(), 1); QCOMPARE(dragMotionSpy.first().first().toPointF(), QPointF(3, 3)); QCOMPARE(dragMotionSpy.first().last().toUInt(), 3u); // simulate drop - QSignalSpy leftSpy(m_dataDevice, &DataDevice::dragLeft); - QVERIFY(leftSpy.isValid()); QSignalSpy serverDragEndedSpy(m_seatInterface, &SeatInterface::dragEnded); QVERIFY(serverDragEndedSpy.isValid()); QSignalSpy droppedSpy(m_dataDevice, &DataDevice::dropped); QVERIFY(droppedSpy.isValid()); m_seatInterface->setTimestamp(4); m_seatInterface->pointerButtonReleased(1); + QVERIFY(sourceDropSpy.isEmpty()); QVERIFY(droppedSpy.wait()); - QCOMPARE(leftSpy.count(), 1); + QCOMPARE(sourceDropSpy.count(), 1); QCOMPARE(serverDragEndedSpy.count(), 1); + QSignalSpy finishedSpy(m_dataSource, &DataSource::dragAndDropFinished); + QVERIFY(finishedSpy.isValid()); + offer->dragAndDropFinished(); + QVERIFY(finishedSpy.wait()); + delete offer; + // verify that we did not get any further input events QVERIFY(pointerMotionSpy.isEmpty()); QCOMPARE(buttonPressSpy.count(), 1); } void TestDragAndDrop::testPointerEventsIgnored() { // this test verifies that all pointer events are ignored on the focused Pointer device during drag using namespace KWayland::Server; using namespace KWayland::Client; // first create a window QScopedPointer s(createSurface()); auto serverSurface = getServerSurface(); QVERIFY(serverSurface); // pass it pointer focus m_seatInterface->setFocusedPointerSurface(serverSurface); // create signal spies for all the pointer events QSignalSpy pointerEnteredSpy(m_pointer, &Pointer::entered); QVERIFY(pointerEnteredSpy.isValid()); QSignalSpy pointerLeftSpy(m_pointer, &Pointer::left); QVERIFY(pointerLeftSpy.isValid()); QSignalSpy pointerMotionSpy(m_pointer, &Pointer::motion); QVERIFY(pointerMotionSpy.isValid()); QSignalSpy axisSpy(m_pointer, &Pointer::axisChanged); QVERIFY(axisSpy.isValid()); QSignalSpy buttonSpy(m_pointer, &Pointer::buttonStateChanged); QVERIFY(buttonSpy.isValid()); QSignalSpy dragEnteredSpy(m_dataDevice, &DataDevice::dragEntered); QVERIFY(dragEnteredSpy.isValid()); // first simulate a few things quint32 timestamp = 1; m_seatInterface->setTimestamp(timestamp++); m_seatInterface->setPointerPos(QPointF(10, 10)); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerAxis(Qt::Vertical, 5); // verify that we have those QVERIFY(axisSpy.wait()); QCOMPARE(axisSpy.count(), 1); QCOMPARE(pointerMotionSpy.count(), 1); QCOMPARE(pointerEnteredSpy.count(), 1); QVERIFY(buttonSpy.isEmpty()); QVERIFY(pointerLeftSpy.isEmpty()); // let's start the drag m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerButtonPressed(1); QVERIFY(buttonSpy.wait()); QCOMPARE(buttonSpy.count(), 1); m_dataDevice->startDrag(buttonSpy.first().first().value(), m_dataSource, s.data()); QVERIFY(dragEnteredSpy.wait()); // now simulate all the possible pointer interactions m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerButtonPressed(2); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerButtonReleased(2); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerAxis(Qt::Vertical, 5); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerAxis(Qt::Horizontal, 5); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->setFocusedPointerSurface(nullptr); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->setFocusedPointerSurface(serverSurface); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->setPointerPos(QPointF(50, 50)); // last but not least, simulate the drop QSignalSpy droppedSpy(m_dataDevice, &DataDevice::dropped); QVERIFY(droppedSpy.isValid()); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerButtonReleased(1); QVERIFY(droppedSpy.wait()); // all the changes should have been ignored QCOMPARE(axisSpy.count(), 1); QCOMPARE(pointerMotionSpy.count(), 1); QCOMPARE(pointerEnteredSpy.count(), 1); QCOMPARE(buttonSpy.count(), 1); QVERIFY(pointerLeftSpy.isEmpty()); } QTEST_GUILESS_MAIN(TestDragAndDrop) #include "test_drag_drop.moc" diff --git a/autotests/client/test_idle.cpp b/autotests/client/test_idle.cpp index 3474b5d..18169c9 100644 --- a/autotests/client/test_idle.cpp +++ b/autotests/client/test_idle.cpp @@ -1,190 +1,289 @@ /******************************************************************** Copyright 2016 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ // Qt #include // client #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/idle.h" #include "../../src/client/registry.h" #include "../../src/client/seat.h" // server #include "../../src/server/display.h" #include "../../src/server/idle_interface.h" #include "../../src/server/seat_interface.h" using namespace KWayland::Client; using namespace KWayland::Server; class IdleTest : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testTimeout(); void testSimulateUserActivity(); + void testServerSimulateUserActivity(); + void testIdleInhibit(); + void testIdleInhibitBlocksTimeout(); private: Display *m_display = nullptr; SeatInterface *m_seatInterface = nullptr; IdleInterface *m_idleInterface = nullptr; ConnectionThread *m_connection = nullptr; QThread *m_thread = nullptr; EventQueue *m_queue = nullptr; Seat *m_seat = nullptr; Idle *m_idle = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-idle-0"); void IdleTest::init() { delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_display->createShm(); m_seatInterface = m_display->createSeat(); m_seatInterface->setName(QStringLiteral("seat0")); m_seatInterface->create(); m_idleInterface = m_display->createIdle(); m_idleInterface->create(); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new EventQueue(this); m_queue->setup(m_connection); Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); registry.setEventQueue(m_queue); registry.create(m_connection); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(interfacesAnnouncedSpy.wait()); m_seat = registry.createSeat(registry.interface(Registry::Interface::Seat).name, registry.interface(Registry::Interface::Seat).version, this); QVERIFY(m_seat->isValid()); m_idle = registry.createIdle(registry.interface(Registry::Interface::Idle).name, registry.interface(Registry::Interface::Idle).version, this); QVERIFY(m_idle->isValid()); } void IdleTest::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(m_idle) CLEANUP(m_seat) CLEANUP(m_queue) if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } CLEANUP(m_idleInterface) CLEANUP(m_seatInterface) CLEANUP(m_display) #undef CLEANUP } void IdleTest::testTimeout() { // this test verifies the basic functionality of a timeout, that it gets fired // and that it resumes from idle, etc. QScopedPointer timeout(m_idle->getTimeout(1, m_seat)); QVERIFY(timeout->isValid()); QSignalSpy idleSpy(timeout.data(), &IdleTimeout::idle); QVERIFY(idleSpy.isValid()); QSignalSpy resumedFormIdleSpy(timeout.data(), &IdleTimeout::resumeFromIdle); QVERIFY(resumedFormIdleSpy.isValid()); // we requested a timeout of 1 msec, but the minimum the server sets is 5 sec QVERIFY(!idleSpy.wait(500)); // the default of 5 sec will now pass QVERIFY(idleSpy.wait()); // simulate some activity QVERIFY(resumedFormIdleSpy.isEmpty()); m_seatInterface->setTimestamp(1); QVERIFY(resumedFormIdleSpy.wait()); timeout.reset(); m_connection->flush(); m_display->dispatchEvents(); } void IdleTest::testSimulateUserActivity() { // this test verifies that simulate user activity doesn't fire the timer QScopedPointer timeout(m_idle->getTimeout(6000, m_seat)); QVERIFY(timeout->isValid()); QSignalSpy idleSpy(timeout.data(), &IdleTimeout::idle); QVERIFY(idleSpy.isValid()); QSignalSpy resumedFormIdleSpy(timeout.data(), &IdleTimeout::resumeFromIdle); QVERIFY(resumedFormIdleSpy.isValid()); m_connection->flush(); QTest::qWait(4000); timeout->simulateUserActivity(); // waiting default five sec should fail QVERIFY(!idleSpy.wait()); // another 2 sec should fire QVERIFY(idleSpy.wait(2000)); // now simulating user activity should emit a resumedFromIdle QVERIFY(resumedFormIdleSpy.isEmpty()); timeout->simulateUserActivity(); QVERIFY(resumedFormIdleSpy.wait()); timeout.reset(); m_connection->flush(); m_display->dispatchEvents(); } +void IdleTest::testServerSimulateUserActivity() +{ + // this test verifies that simulate user activity doesn't fire the timer + QScopedPointer timeout(m_idle->getTimeout(6000, m_seat)); + QVERIFY(timeout->isValid()); + QSignalSpy idleSpy(timeout.data(), &IdleTimeout::idle); + QVERIFY(idleSpy.isValid()); + QSignalSpy resumedFormIdleSpy(timeout.data(), &IdleTimeout::resumeFromIdle); + QVERIFY(resumedFormIdleSpy.isValid()); + m_connection->flush(); + + QTest::qWait(4000); + m_idleInterface->simulateUserActivity(); + // waiting default five sec should fail + QVERIFY(!idleSpy.wait()); + // another 2 sec should fire + QVERIFY(idleSpy.wait(2000)); + + // now simulating user activity should emit a resumedFromIdle + QVERIFY(resumedFormIdleSpy.isEmpty()); + m_idleInterface->simulateUserActivity(); + QVERIFY(resumedFormIdleSpy.wait()); + + timeout.reset(); + m_connection->flush(); + m_display->dispatchEvents(); +} + +void IdleTest::testIdleInhibit() +{ + QCOMPARE(m_idleInterface->isInhibited(), false); + QSignalSpy idleInhibitedSpy(m_idleInterface, &IdleInterface::inhibitedChanged); + QVERIFY(idleInhibitedSpy.isValid()); + m_idleInterface->inhibit(); + QCOMPARE(m_idleInterface->isInhibited(), true); + QCOMPARE(idleInhibitedSpy.count(), 1); + m_idleInterface->inhibit(); + QCOMPARE(m_idleInterface->isInhibited(), true); + QCOMPARE(idleInhibitedSpy.count(), 1); + m_idleInterface->uninhibit(); + QCOMPARE(m_idleInterface->isInhibited(), true); + QCOMPARE(idleInhibitedSpy.count(), 1); + m_idleInterface->uninhibit(); + QCOMPARE(m_idleInterface->isInhibited(), false); + QCOMPARE(idleInhibitedSpy.count(), 2); +} + +void IdleTest::testIdleInhibitBlocksTimeout() +{ + // this test verifies that a timeout does not fire when the system is inhibited + + // so first inhibit + QCOMPARE(m_idleInterface->isInhibited(), false); + m_idleInterface->inhibit(); + + QScopedPointer timeout(m_idle->getTimeout(1, m_seat)); + QVERIFY(timeout->isValid()); + QSignalSpy idleSpy(timeout.data(), &IdleTimeout::idle); + QVERIFY(idleSpy.isValid()); + QSignalSpy resumedFormIdleSpy(timeout.data(), &IdleTimeout::resumeFromIdle); + QVERIFY(resumedFormIdleSpy.isValid()); + + // we requested a timeout of 1 msec, but the minimum the server sets is 5 sec + QVERIFY(!idleSpy.wait(500)); + // the default of 5 sec won't pass + QVERIFY(!idleSpy.wait()); + + // simulate some activity + QVERIFY(resumedFormIdleSpy.isEmpty()); + m_seatInterface->setTimestamp(1); + // resume from idle should not fire + QVERIFY(!resumedFormIdleSpy.wait()); + + // let's uninhibit + m_idleInterface->uninhibit(); + QCOMPARE(m_idleInterface->isInhibited(), false); + // we requested a timeout of 1 msec, but the minimum the server sets is 5 sec + QVERIFY(!idleSpy.wait(500)); + // the default of 5 sec will now pass + QVERIFY(idleSpy.wait()); + + // if we inhibit now it will trigger a resume from idle + QVERIFY(resumedFormIdleSpy.isEmpty()); + m_idleInterface->inhibit(); + QVERIFY(resumedFormIdleSpy.wait()); + + // let's wait again just to verify that also inhibit for already existing IdleTimeout works + QVERIFY(!idleSpy.wait(500)); + QVERIFY(!idleSpy.wait()); + QCOMPARE(idleSpy.count(), 1); + + timeout.reset(); + m_connection->flush(); + m_display->dispatchEvents(); +} + QTEST_GUILESS_MAIN(IdleTest) #include "test_idle.moc" diff --git a/autotests/client/test_server_side_decoration_palette.cpp b/autotests/client/test_server_side_decoration_palette.cpp new file mode 100644 index 0000000..2d808ef --- /dev/null +++ b/autotests/client/test_server_side_decoration_palette.cpp @@ -0,0 +1,191 @@ +/******************************************************************** +Copyright 2017 David Edmundson +Copyright 2014 Martin Gräßlin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +*********************************************************************/ +// Qt +#include +// KWin +#include "../../src/client/compositor.h" +#include "../../src/client/connection_thread.h" +#include "../../src/client/event_queue.h" +#include "../../src/client/region.h" +#include "../../src/client/registry.h" +#include "../../src/client/surface.h" +#include "../../src/client/server_decoration_palette.h" +#include "../../src/server/display.h" +#include "../../src/server/compositor_interface.h" +#include "../../src/server/region_interface.h" +#include "../../src/server/server_decoration_palette_interface.h" + +using namespace KWayland::Client; + +class TestServerSideDecorationPalette : public QObject +{ + Q_OBJECT +public: + explicit TestServerSideDecorationPalette(QObject *parent = nullptr); +private Q_SLOTS: + void init(); + void cleanup(); + + void testCreateAndSet(); + +private: + KWayland::Server::Display *m_display; + KWayland::Server::CompositorInterface *m_compositorInterface; + KWayland::Server::ServerSideDecorationPaletteManagerInterface *m_paletteManagerInterface; + KWayland::Client::ConnectionThread *m_connection; + KWayland::Client::Compositor *m_compositor; + KWayland::Client::ServerSideDecorationPaletteManager *m_paletteManager; + KWayland::Client::EventQueue *m_queue; + QThread *m_thread; +}; + +static const QString s_socketName = QStringLiteral("kwayland-test-wayland-decopalette-0"); + +TestServerSideDecorationPalette::TestServerSideDecorationPalette(QObject *parent) + : QObject(parent) + , m_display(nullptr) + , m_compositorInterface(nullptr) + , m_connection(nullptr) + , m_compositor(nullptr) + , m_queue(nullptr) + , m_thread(nullptr) +{ +} + +void TestServerSideDecorationPalette::init() +{ + using namespace KWayland::Server; + delete m_display; + m_display = new Display(this); + m_display->setSocketName(s_socketName); + m_display->start(); + QVERIFY(m_display->isRunning()); + + // setup connection + m_connection = new KWayland::Client::ConnectionThread; + QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); + QVERIFY(connectedSpy.isValid()); + m_connection->setSocketName(s_socketName); + + m_thread = new QThread(this); + m_connection->moveToThread(m_thread); + m_thread->start(); + + m_connection->initConnection(); + QVERIFY(connectedSpy.wait()); + + m_queue = new KWayland::Client::EventQueue(this); + QVERIFY(!m_queue->isValid()); + m_queue->setup(m_connection); + QVERIFY(m_queue->isValid()); + + Registry registry; + QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); + QVERIFY(compositorSpy.isValid()); + + QSignalSpy registrySpy(®istry, &Registry::serverSideDecorationPaletteManagerAnnounced); + QVERIFY(registrySpy.isValid()); + + QVERIFY(!registry.eventQueue()); + registry.setEventQueue(m_queue); + QCOMPARE(registry.eventQueue(), m_queue); + registry.create(m_connection->display()); + QVERIFY(registry.isValid()); + registry.setup(); + + m_compositorInterface = m_display->createCompositor(m_display); + m_compositorInterface->create(); + QVERIFY(m_compositorInterface->isValid()); + + QVERIFY(compositorSpy.wait()); + m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); + + m_paletteManagerInterface = m_display->createServerSideDecorationPaletteManager(m_display); + m_paletteManagerInterface->create(); + QVERIFY(m_paletteManagerInterface->isValid()); + + QVERIFY(registrySpy.wait()); + m_paletteManager = registry.createServerSideDecorationPaletteManager(registrySpy.first().first().value(), registrySpy.first().last().value(), this); +} + +void TestServerSideDecorationPalette::cleanup() +{ +#define CLEANUP(variable) \ + if (variable) { \ + delete variable; \ + variable = nullptr; \ + } + CLEANUP(m_compositor) + CLEANUP(m_paletteManager) + CLEANUP(m_queue) + if (m_connection) { + m_connection->deleteLater(); + m_connection = nullptr; + } + if (m_thread) { + m_thread->quit(); + m_thread->wait(); + delete m_thread; + m_thread = nullptr; + } + CLEANUP(m_compositorInterface) + CLEANUP(m_paletteManagerInterface) + CLEANUP(m_display) +#undef CLEANUP +} + +void TestServerSideDecorationPalette::testCreateAndSet() +{ + QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); + QVERIFY(serverSurfaceCreated.isValid()); + + QScopedPointer surface(m_compositor->createSurface()); + QVERIFY(serverSurfaceCreated.wait()); + + auto serverSurface = serverSurfaceCreated.first().first().value(); + QSignalSpy paletteCreatedSpy(m_paletteManagerInterface, &KWayland::Server::ServerSideDecorationPaletteManagerInterface::paletteCreated); + + QVERIFY(!m_paletteManagerInterface->paletteForSurface(serverSurface)); + + auto palette = m_paletteManager->create(surface.data(), surface.data()); + QVERIFY(paletteCreatedSpy.wait()); + auto paletteInterface = paletteCreatedSpy.first().first().value(); + QCOMPARE(m_paletteManagerInterface->paletteForSurface(serverSurface), paletteInterface); + + QCOMPARE(paletteInterface->palette(), QString()); + + QSignalSpy changedSpy(paletteInterface, &KWayland::Server::ServerSideDecorationPaletteInterface::paletteChanged); + + palette->setPalette("foobar"); + + QVERIFY(changedSpy.wait()); + QCOMPARE(paletteInterface->palette(), QString("foobar")); + + // and destroy + QSignalSpy destroyedSpy(paletteInterface, &QObject::destroyed); + QVERIFY(destroyedSpy.isValid()); + delete palette; + QVERIFY(destroyedSpy.wait()); + QVERIFY(!m_paletteManagerInterface->paletteForSurface(serverSurface)); +} + +QTEST_GUILESS_MAIN(TestServerSideDecorationPalette) +#include "test_server_side_decoration_palette.moc" diff --git a/autotests/client/test_wayland_appmenu.cpp b/autotests/client/test_wayland_appmenu.cpp new file mode 100644 index 0000000..8ba19fa --- /dev/null +++ b/autotests/client/test_wayland_appmenu.cpp @@ -0,0 +1,196 @@ +/******************************************************************** +Copyright 2017 David Edmundson +Copyright 2014 Martin Gräßlin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +*********************************************************************/ +// Qt +#include +// KWin +#include "../../src/client/compositor.h" +#include "../../src/client/connection_thread.h" +#include "../../src/client/event_queue.h" +#include "../../src/client/region.h" +#include "../../src/client/registry.h" +#include "../../src/client/surface.h" +#include "../../src/client/appmenu.h" +#include "../../src/server/display.h" +#include "../../src/server/compositor_interface.h" +#include "../../src/server/region_interface.h" +#include "../../src/server/appmenu_interface.h" + +using namespace KWayland::Client; + +Q_DECLARE_METATYPE(KWayland::Server::AppMenuInterface::InterfaceAddress) + +class TestAppmenu : public QObject +{ + Q_OBJECT +public: + explicit TestAppmenu(QObject *parent = nullptr); +private Q_SLOTS: + void init(); + void cleanup(); + + void testCreateAndSet(); + +private: + KWayland::Server::Display *m_display; + KWayland::Server::CompositorInterface *m_compositorInterface; + KWayland::Server::AppMenuManagerInterface *m_appmenuManagerInterface; + KWayland::Client::ConnectionThread *m_connection; + KWayland::Client::Compositor *m_compositor; + KWayland::Client::AppMenuManager *m_appmenuManager; + KWayland::Client::EventQueue *m_queue; + QThread *m_thread; +}; + +static const QString s_socketName = QStringLiteral("kwayland-test-wayland-appmenu-0"); + +TestAppmenu::TestAppmenu(QObject *parent) + : QObject(parent) + , m_display(nullptr) + , m_compositorInterface(nullptr) + , m_connection(nullptr) + , m_compositor(nullptr) + , m_queue(nullptr) + , m_thread(nullptr) +{ +} + +void TestAppmenu::init() +{ + using namespace KWayland::Server; + qRegisterMetaType(); + delete m_display; + m_display = new Display(this); + m_display->setSocketName(s_socketName); + m_display->start(); + QVERIFY(m_display->isRunning()); + + // setup connection + m_connection = new KWayland::Client::ConnectionThread; + QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); + QVERIFY(connectedSpy.isValid()); + m_connection->setSocketName(s_socketName); + + m_thread = new QThread(this); + m_connection->moveToThread(m_thread); + m_thread->start(); + + m_connection->initConnection(); + QVERIFY(connectedSpy.wait()); + + m_queue = new KWayland::Client::EventQueue(this); + QVERIFY(!m_queue->isValid()); + m_queue->setup(m_connection); + QVERIFY(m_queue->isValid()); + + Registry registry; + QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); + QVERIFY(compositorSpy.isValid()); + + QSignalSpy appmenuSpy(®istry, &Registry::appMenuAnnounced); + QVERIFY(appmenuSpy.isValid()); + + QVERIFY(!registry.eventQueue()); + registry.setEventQueue(m_queue); + QCOMPARE(registry.eventQueue(), m_queue); + registry.create(m_connection->display()); + QVERIFY(registry.isValid()); + registry.setup(); + + m_compositorInterface = m_display->createCompositor(m_display); + m_compositorInterface->create(); + QVERIFY(m_compositorInterface->isValid()); + + QVERIFY(compositorSpy.wait()); + m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); + + m_appmenuManagerInterface = m_display->createAppMenuManagerInterface(m_display); + m_appmenuManagerInterface->create(); + QVERIFY(m_appmenuManagerInterface->isValid()); + + QVERIFY(appmenuSpy.wait()); + m_appmenuManager = registry.createAppMenuManager(appmenuSpy.first().first().value(), appmenuSpy.first().last().value(), this); +} + +void TestAppmenu::cleanup() +{ +#define CLEANUP(variable) \ + if (variable) { \ + delete variable; \ + variable = nullptr; \ + } + CLEANUP(m_compositor) + CLEANUP(m_appmenuManager) + CLEANUP(m_queue) + if (m_connection) { + m_connection->deleteLater(); + m_connection = nullptr; + } + if (m_thread) { + m_thread->quit(); + m_thread->wait(); + delete m_thread; + m_thread = nullptr; + } + CLEANUP(m_compositorInterface) + CLEANUP(m_appmenuManagerInterface) + CLEANUP(m_display) +#undef CLEANUP +} + +void TestAppmenu::testCreateAndSet() +{ + QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); + QVERIFY(serverSurfaceCreated.isValid()); + + QScopedPointer surface(m_compositor->createSurface()); + QVERIFY(serverSurfaceCreated.wait()); + + auto serverSurface = serverSurfaceCreated.first().first().value(); + QSignalSpy appMenuCreated(m_appmenuManagerInterface, &KWayland::Server::AppMenuManagerInterface::appMenuCreated); + + QVERIFY(!m_appmenuManagerInterface->appMenuForSurface(serverSurface)); + + auto appmenu = m_appmenuManager->create(surface.data(), surface.data()); + QVERIFY(appMenuCreated.wait()); + auto appMenuInterface = appMenuCreated.first().first().value(); + QCOMPARE(m_appmenuManagerInterface->appMenuForSurface(serverSurface), appMenuInterface); + + QCOMPARE(appMenuInterface->address().serviceName, QString()); + QCOMPARE(appMenuInterface->address().objectPath, QString()); + + QSignalSpy appMenuChangedSpy(appMenuInterface, &KWayland::Server::AppMenuInterface::addressChanged); + + appmenu->setAddress("net.somename", "/test/path"); + + QVERIFY(appMenuChangedSpy.wait()); + QCOMPARE(appMenuInterface->address().serviceName, QString("net.somename")); + QCOMPARE(appMenuInterface->address().objectPath, QString("/test/path")); + + // and destroy + QSignalSpy destroyedSpy(appMenuInterface, &QObject::destroyed); + QVERIFY(destroyedSpy.isValid()); + delete appmenu; + QVERIFY(destroyedSpy.wait()); + QVERIFY(!m_appmenuManagerInterface->appMenuForSurface(serverSurface)); +} + +QTEST_GUILESS_MAIN(TestAppmenu) +#include "test_wayland_appmenu.moc" diff --git a/autotests/client/test_wayland_filter.cpp b/autotests/client/test_wayland_filter.cpp new file mode 100644 index 0000000..dd6a8aa --- /dev/null +++ b/autotests/client/test_wayland_filter.cpp @@ -0,0 +1,168 @@ +/******************************************************************** +Copyright 2017 David Edmundson + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +*********************************************************************/ +// Qt +#include +// KWin +#include "../../src/client/compositor.h" +#include "../../src/client/connection_thread.h" +#include "../../src/client/event_queue.h" +#include "../../src/client/region.h" +#include "../../src/client/registry.h" +#include "../../src/client/surface.h" +#include "../../src/client/blur.h" +#include "../../src/server/display.h" +#include "../../src/server/compositor_interface.h" +#include "../../src/server/region_interface.h" +#include "../../src/server/blur_interface.h" +#include "../../src/server/filtered_display.h" + +#include + +using namespace KWayland::Client; + +class TestDisplay; + +class TestFilter : public QObject +{ + Q_OBJECT +public: + explicit TestFilter(QObject *parent = nullptr); +private Q_SLOTS: + void init(); + void cleanup(); + void testFilter_data(); + void testFilter(); + +private: + TestDisplay *m_display; + KWayland::Server::CompositorInterface *m_compositorInterface; + KWayland::Server::BlurManagerInterface *m_blurManagerInterface; +}; + +static const QString s_socketName = QStringLiteral("kwayland-test-wayland-blur-0"); + +//The following non-realistic class allows only clients in the m_allowedClients list to access the blur interface +//all other interfaces are allowed +class TestDisplay : public KWayland::Server::FilteredDisplay +{ +public: + TestDisplay(QObject *parent); + bool allowInterface(KWayland::Server::ClientConnection * client, const QByteArray & interfaceName) override; + QList m_allowedClients; +}; + +TestDisplay::TestDisplay(QObject *parent): + KWayland::Server::FilteredDisplay(parent) +{} + +bool TestDisplay::allowInterface(KWayland::Server::ClientConnection* client, const QByteArray& interfaceName) +{ + if (interfaceName == "org_kde_kwin_blur_manager") { + return m_allowedClients.contains(*client); + } + return true; +} + +TestFilter::TestFilter(QObject *parent) + : QObject(parent) + , m_display(nullptr) + , m_compositorInterface(nullptr) +{} + +void TestFilter::init() +{ + using namespace KWayland::Server; + delete m_display; + m_display = new TestDisplay(this); + m_display->setSocketName(s_socketName); + m_display->start(); + QVERIFY(m_display->isRunning()); + + m_compositorInterface = m_display->createCompositor(m_display); + m_compositorInterface->create(); + QVERIFY(m_compositorInterface->isValid()); + + m_blurManagerInterface = m_display->createBlurManager(m_display); + m_blurManagerInterface->create(); + QVERIFY(m_blurManagerInterface->isValid()); +} + +void TestFilter::cleanup() +{ +} + +void TestFilter::testFilter_data() +{ + QTest::addColumn("accessAllowed"); + QTest::newRow("granted") << true; + QTest::newRow("denied") << false; + +} + +void TestFilter::testFilter() +{ + QFETCH(bool, accessAllowed); + + // setup connection + QScopedPointer connection(new KWayland::Client::ConnectionThread()); + QSignalSpy connectedSpy(connection.data(), &ConnectionThread::connected); + QVERIFY(connectedSpy.isValid()); + connection->setSocketName(s_socketName); + + QScopedPointer thread(new QThread(this)); + connection->moveToThread(thread.data()); + thread->start(); + + connection->initConnection(); + QVERIFY(connectedSpy.wait()); + + //use low level API as Server::Display::connections only lists connections which have + //been previous fetched via getConnection() + if (accessAllowed) { + wl_client *clientConnection; + wl_client_for_each(clientConnection, wl_display_get_client_list(*m_display)) { + m_display->m_allowedClients << clientConnection; + } + } + + KWayland::Client::EventQueue queue; + queue.setup(connection.data()); + + Registry registry; + QSignalSpy registryDoneSpy(®istry, &Registry::interfacesAnnounced); + QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); + QSignalSpy blurSpy(®istry, &Registry::blurAnnounced); + + registry.setEventQueue(&queue); + registry.create(connection->display()); + QVERIFY(registry.isValid()); + registry.setup(); + + QVERIFY(registryDoneSpy.wait()); + QVERIFY(compositorSpy.count() == 1); + QVERIFY(blurSpy.count() == accessAllowed ? 1 : 0); + + thread->quit(); + thread->wait(); +} + + +QTEST_GUILESS_MAIN(TestFilter) +#include "test_wayland_filter.moc" diff --git a/autotests/client/test_wayland_registry.cpp b/autotests/client/test_wayland_registry.cpp index d7804b5..d73baf6 100644 --- a/autotests/client/test_wayland_registry.cpp +++ b/autotests/client/test_wayland_registry.cpp @@ -1,850 +1,881 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ // Qt #include // KWin #include "../../src/client/blur.h" #include "../../src/client/contrast.h" #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/dpms.h" #include "../../src/client/event_queue.h" #include "../../src/client/registry.h" #include "../../src/client/output.h" #include "../../src/client/pointerconstraints.h" #include "../../src/client/pointergestures.h" +#include "../../src/client/idleinhibit.h" #include "../../src/client/seat.h" #include "../../src/client/relativepointer.h" #include "../../src/client/server_decoration.h" #include "../../src/client/shell.h" #include "../../src/client/surface.h" #include "../../src/client/subcompositor.h" #include "../../src/client/xdgshell.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/datadevicemanager_interface.h" #include "../../src/server/display.h" #include "../../src/server/dpms_interface.h" +#include "../../src/server/idleinhibit_interface.h" #include "../../src/server/output_interface.h" #include "../../src/server/seat_interface.h" #include "../../src/server/shell_interface.h" #include "../../src/server/blur_interface.h" #include "../../src/server/contrast_interface.h" #include "../../src/server/server_decoration_interface.h" #include "../../src/server/slide_interface.h" #include "../../src/server/subcompositor_interface.h" #include "../../src/server/outputmanagement_interface.h" #include "../../src/server/outputdevice_interface.h" #include "../../src/server/pointerconstraints_interface.h" #include "../../src/server/pointergestures_interface.h" #include "../../src/server/textinput_interface.h" #include "../../src/server/xdgshell_interface.h" #include "../../src/server/relativepointer_interface.h" // Wayland #include #include +#include #include #include #include #include #include #include #include class TestWaylandRegistry : public QObject { Q_OBJECT public: explicit TestWaylandRegistry(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testCreate(); void testBindCompositor(); void testBindShell(); void testBindOutput(); void testBindShm(); void testBindSeat(); void testBindSubCompositor(); void testBindDataDeviceManager(); void testBindBlurManager(); void testBindContrastManager(); void testBindSlideManager(); void testBindDpmsManager(); void testBindServerSideDecorationManager(); void testBindTextInputManagerUnstableV0(); void testBindTextInputManagerUnstableV2(); void testBindXdgShellUnstableV5(); void testBindRelativePointerManagerUnstableV1(); void testBindPointerGesturesUnstableV1(); void testBindPointerConstraintsUnstableV1(); + void testBindIdleIhibitManagerUnstableV1(); void testGlobalSync(); void testGlobalSyncThreaded(); void testRemoval(); void testOutOfSyncRemoval(); void testDestroy(); void testAnnounceMultiple(); void testAnnounceMultipleOutputDevices(); private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositor; KWayland::Server::OutputInterface *m_output; KWayland::Server::OutputDeviceInterface *m_outputDevice; KWayland::Server::SeatInterface *m_seat; KWayland::Server::ShellInterface *m_shell; KWayland::Server::SubCompositorInterface *m_subcompositor; KWayland::Server::DataDeviceManagerInterface *m_dataDeviceManager; KWayland::Server::OutputManagementInterface *m_outputManagement; KWayland::Server::ServerSideDecorationManagerInterface *m_serverSideDecorationManager; KWayland::Server::TextInputManagerInterface *m_textInputManagerV0; KWayland::Server::TextInputManagerInterface *m_textInputManagerV2; KWayland::Server::XdgShellInterface *m_xdgShellUnstableV5; KWayland::Server::RelativePointerManagerInterface *m_relativePointerV1; KWayland::Server::PointerGesturesInterface *m_pointerGesturesV1; KWayland::Server::PointerConstraintsInterface *m_pointerConstraintsV1; KWayland::Server::BlurManagerInterface *m_blur; KWayland::Server::ContrastManagerInterface *m_contrast; + KWayland::Server::IdleInhibitManagerInterface *m_idleInhibit; }; static const QString s_socketName = QStringLiteral("kwin-test-wayland-registry-0"); TestWaylandRegistry::TestWaylandRegistry(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositor(nullptr) , m_output(nullptr) , m_outputDevice(nullptr) , m_seat(nullptr) , m_shell(nullptr) , m_subcompositor(nullptr) , m_dataDeviceManager(nullptr) , m_outputManagement(nullptr) , m_serverSideDecorationManager(nullptr) , m_textInputManagerV0(nullptr) , m_textInputManagerV2(nullptr) , m_xdgShellUnstableV5(nullptr) , m_relativePointerV1(nullptr) , m_pointerGesturesV1(nullptr) , m_pointerConstraintsV1(nullptr) , m_blur(nullptr) , m_contrast(nullptr) + , m_idleInhibit(nullptr) { } void TestWaylandRegistry::init() { m_display = new KWayland::Server::Display(); m_display->setSocketName(s_socketName); m_display->start(); m_display->createShm(); m_compositor = m_display->createCompositor(); m_compositor->create(); m_output = m_display->createOutput(); m_output->create(); m_seat = m_display->createSeat(); m_seat->create(); m_shell = m_display->createShell(); m_shell->create(); m_subcompositor = m_display->createSubCompositor(); m_subcompositor->create(); m_dataDeviceManager = m_display->createDataDeviceManager(); m_dataDeviceManager->create(); m_outputManagement = m_display->createOutputManagement(); m_outputManagement->create(); m_outputDevice = m_display->createOutputDevice(); m_outputDevice->create(); QVERIFY(m_outputManagement->isValid()); m_blur = m_display->createBlurManager(this); m_blur->create(); m_contrast = m_display->createContrastManager(this); m_contrast->create(); m_display->createSlideManager(this)->create(); m_display->createDpmsManager()->create(); m_serverSideDecorationManager = m_display->createServerSideDecorationManager(); m_serverSideDecorationManager->create(); m_textInputManagerV0 = m_display->createTextInputManager(KWayland::Server::TextInputInterfaceVersion::UnstableV0); QCOMPARE(m_textInputManagerV0->interfaceVersion(), KWayland::Server::TextInputInterfaceVersion::UnstableV0); m_textInputManagerV0->create(); m_textInputManagerV2 = m_display->createTextInputManager(KWayland::Server::TextInputInterfaceVersion::UnstableV2); QCOMPARE(m_textInputManagerV2->interfaceVersion(), KWayland::Server::TextInputInterfaceVersion::UnstableV2); m_textInputManagerV2->create(); m_xdgShellUnstableV5 = m_display->createXdgShell(KWayland::Server::XdgShellInterfaceVersion::UnstableV5); m_xdgShellUnstableV5->create(); QCOMPARE(m_xdgShellUnstableV5->interfaceVersion(), KWayland::Server::XdgShellInterfaceVersion::UnstableV5); m_relativePointerV1 = m_display->createRelativePointerManager(KWayland::Server::RelativePointerInterfaceVersion::UnstableV1); m_relativePointerV1->create(); QCOMPARE(m_relativePointerV1->interfaceVersion(), KWayland::Server::RelativePointerInterfaceVersion::UnstableV1); m_pointerGesturesV1 = m_display->createPointerGestures(KWayland::Server::PointerGesturesInterfaceVersion::UnstableV1); m_pointerGesturesV1->create(); QCOMPARE(m_pointerGesturesV1->interfaceVersion(), KWayland::Server::PointerGesturesInterfaceVersion::UnstableV1); m_pointerConstraintsV1 = m_display->createPointerConstraints(KWayland::Server::PointerConstraintsInterfaceVersion::UnstableV1); m_pointerConstraintsV1->create(); QCOMPARE(m_pointerConstraintsV1->interfaceVersion(), KWayland::Server::PointerConstraintsInterfaceVersion::UnstableV1); + m_idleInhibit = m_display->createIdleInhibitManager(KWayland::Server::IdleInhibitManagerInterfaceVersion::UnstableV1); + m_idleInhibit->create(); + QCOMPARE(m_idleInhibit->interfaceVersion(), KWayland::Server::IdleInhibitManagerInterfaceVersion::UnstableV1); } void TestWaylandRegistry::cleanup() { delete m_display; m_display = nullptr; } void TestWaylandRegistry::testCreate() { KWayland::Client::ConnectionThread connection; QSignalSpy connectedSpy(&connection, SIGNAL(connected())); connection.setSocketName(s_socketName); connection.initConnection(); QVERIFY(connectedSpy.wait()); KWayland::Client::Registry registry; QVERIFY(!registry.isValid()); registry.create(connection.display()); QVERIFY(registry.isValid()); registry.release(); QVERIFY(!registry.isValid()); } #define TEST_BIND(iface, signalName, bindMethod, destroyFunction) \ KWayland::Client::ConnectionThread connection; \ QSignalSpy connectedSpy(&connection, SIGNAL(connected())); \ connection.setSocketName(s_socketName); \ connection.initConnection(); \ QVERIFY(connectedSpy.wait()); \ \ KWayland::Client::Registry registry; \ /* before registry is created, we cannot bind the interface*/ \ QVERIFY(!registry.bindMethod(0, 0)); \ \ QVERIFY(!registry.isValid()); \ registry.create(&connection); \ QVERIFY(registry.isValid()); \ /* created but not yet connected still results in no bind */ \ QVERIFY(!registry.bindMethod(0, 0)); \ /* interface information should be empty */ \ QVERIFY(registry.interfaces(iface).isEmpty()); \ QCOMPARE(registry.interface(iface).name, 0u); \ QCOMPARE(registry.interface(iface).version, 0u); \ \ /* now let's register */ \ QSignalSpy announced(®istry, signalName); \ QVERIFY(announced.isValid()); \ registry.setup(); \ wl_display_flush(connection.display()); \ QVERIFY(announced.wait()); \ const quint32 name = announced.first().first().value(); \ const quint32 version = announced.first().last().value(); \ QCOMPARE(registry.interfaces(iface).count(), 1); \ QCOMPARE(registry.interfaces(iface).first().name, name); \ QCOMPARE(registry.interfaces(iface).first().version, version); \ QCOMPARE(registry.interface(iface).name, name); \ QCOMPARE(registry.interface(iface).version, version); \ \ /* registry should know about the interface now */ \ QVERIFY(registry.hasInterface(iface)); \ QVERIFY(!registry.bindMethod(name+1, version)); \ QVERIFY(registry.bindMethod(name, version+1)); \ auto *c = registry.bindMethod(name, version); \ QVERIFY(c); \ destroyFunction(c); \ void TestWaylandRegistry::testBindCompositor() { TEST_BIND(KWayland::Client::Registry::Interface::Compositor, SIGNAL(compositorAnnounced(quint32,quint32)), bindCompositor, wl_compositor_destroy) } void TestWaylandRegistry::testBindShell() { TEST_BIND(KWayland::Client::Registry::Interface::Shell, SIGNAL(shellAnnounced(quint32,quint32)), bindShell, free) } void TestWaylandRegistry::testBindOutput() { TEST_BIND(KWayland::Client::Registry::Interface::Output, SIGNAL(outputAnnounced(quint32,quint32)), bindOutput, wl_output_destroy) } void TestWaylandRegistry::testBindSeat() { TEST_BIND(KWayland::Client::Registry::Interface::Seat, SIGNAL(seatAnnounced(quint32,quint32)), bindSeat, wl_seat_destroy) } void TestWaylandRegistry::testBindShm() { TEST_BIND(KWayland::Client::Registry::Interface::Shm, SIGNAL(shmAnnounced(quint32,quint32)), bindShm, wl_shm_destroy) } void TestWaylandRegistry::testBindSubCompositor() { TEST_BIND(KWayland::Client::Registry::Interface::SubCompositor, SIGNAL(subCompositorAnnounced(quint32,quint32)), bindSubCompositor, wl_subcompositor_destroy) } void TestWaylandRegistry::testBindDataDeviceManager() { TEST_BIND(KWayland::Client::Registry::Interface::DataDeviceManager, SIGNAL(dataDeviceManagerAnnounced(quint32,quint32)), bindDataDeviceManager, wl_data_device_manager_destroy) } void TestWaylandRegistry::testBindBlurManager() { TEST_BIND(KWayland::Client::Registry::Interface::Blur, SIGNAL(blurAnnounced(quint32,quint32)), bindBlurManager, free) } void TestWaylandRegistry::testBindContrastManager() { TEST_BIND(KWayland::Client::Registry::Interface::Contrast, SIGNAL(contrastAnnounced(quint32,quint32)), bindContrastManager, free) } void TestWaylandRegistry::testBindSlideManager() { TEST_BIND(KWayland::Client::Registry::Interface::Slide, SIGNAL(slideAnnounced(quint32,quint32)), bindSlideManager, free) } void TestWaylandRegistry::testBindDpmsManager() { TEST_BIND(KWayland::Client::Registry::Interface::Dpms, SIGNAL(dpmsAnnounced(quint32,quint32)), bindDpmsManager, org_kde_kwin_dpms_manager_destroy) } void TestWaylandRegistry::testBindServerSideDecorationManager() { TEST_BIND(KWayland::Client::Registry::Interface::ServerSideDecorationManager, SIGNAL(serverSideDecorationManagerAnnounced(quint32,quint32)), bindServerSideDecorationManager, org_kde_kwin_server_decoration_manager_destroy) } void TestWaylandRegistry::testBindTextInputManagerUnstableV0() { TEST_BIND(KWayland::Client::Registry::Interface::TextInputManagerUnstableV0, SIGNAL(textInputManagerUnstableV0Announced(quint32,quint32)), bindTextInputManagerUnstableV0, wl_text_input_manager_destroy) } void TestWaylandRegistry::testBindTextInputManagerUnstableV2() { TEST_BIND(KWayland::Client::Registry::Interface::TextInputManagerUnstableV2, SIGNAL(textInputManagerUnstableV2Announced(quint32,quint32)), bindTextInputManagerUnstableV2, zwp_text_input_manager_v2_destroy) } void TestWaylandRegistry::testBindXdgShellUnstableV5() { TEST_BIND(KWayland::Client::Registry::Interface::XdgShellUnstableV5, SIGNAL(xdgShellUnstableV5Announced(quint32,quint32)), bindXdgShellUnstableV5, xdg_shell_destroy) } void TestWaylandRegistry::testBindRelativePointerManagerUnstableV1() { TEST_BIND(KWayland::Client::Registry::Interface::RelativePointerManagerUnstableV1, SIGNAL(relativePointerManagerUnstableV1Announced(quint32,quint32)), bindRelativePointerManagerUnstableV1, zwp_relative_pointer_manager_v1_destroy) } void TestWaylandRegistry::testBindPointerGesturesUnstableV1() { TEST_BIND(KWayland::Client::Registry::Interface::PointerGesturesUnstableV1, SIGNAL(pointerGesturesUnstableV1Announced(quint32,quint32)), bindPointerGesturesUnstableV1, zwp_pointer_gestures_v1_destroy) } void TestWaylandRegistry::testBindPointerConstraintsUnstableV1() { TEST_BIND(KWayland::Client::Registry::Interface::PointerConstraintsUnstableV1, SIGNAL(pointerConstraintsUnstableV1Announced(quint32,quint32)), bindPointerConstraintsUnstableV1, zwp_pointer_constraints_v1_destroy) } +void TestWaylandRegistry::testBindIdleIhibitManagerUnstableV1() +{ + TEST_BIND(KWayland::Client::Registry::Interface::IdleInhibitManagerUnstableV1, SIGNAL(idleInhibitManagerUnstableV1Announced(quint32,quint32)), bindIdleInhibitManagerUnstableV1, zwp_idle_inhibit_manager_v1_destroy) +} + #undef TEST_BIND void TestWaylandRegistry::testRemoval() { using namespace KWayland::Client; KWayland::Client::ConnectionThread connection; QSignalSpy connectedSpy(&connection, SIGNAL(connected())); connection.setSocketName(s_socketName); connection.initConnection(); QVERIFY(connectedSpy.wait()); connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, &connection, [&connection] { wl_display_flush(connection.display()); } ); KWayland::Client::Registry registry; QSignalSpy shmAnnouncedSpy(®istry, SIGNAL(shmAnnounced(quint32,quint32))); QVERIFY(shmAnnouncedSpy.isValid()); QSignalSpy compositorAnnouncedSpy(®istry, SIGNAL(compositorAnnounced(quint32,quint32))); QVERIFY(compositorAnnouncedSpy.isValid()); QSignalSpy outputAnnouncedSpy(®istry, SIGNAL(outputAnnounced(quint32,quint32))); QVERIFY(outputAnnouncedSpy.isValid()); QSignalSpy outputDeviceAnnouncedSpy(®istry, SIGNAL(outputDeviceAnnounced(quint32,quint32))); QVERIFY(outputDeviceAnnouncedSpy.isValid()); QSignalSpy shellAnnouncedSpy(®istry, SIGNAL(shellAnnounced(quint32,quint32))); QVERIFY(shellAnnouncedSpy.isValid()); QSignalSpy seatAnnouncedSpy(®istry, SIGNAL(seatAnnounced(quint32,quint32))); QVERIFY(seatAnnouncedSpy.isValid()); QSignalSpy subCompositorAnnouncedSpy(®istry, SIGNAL(subCompositorAnnounced(quint32,quint32))); QVERIFY(subCompositorAnnouncedSpy.isValid()); QSignalSpy outputManagementAnnouncedSpy(®istry, SIGNAL(outputManagementAnnounced(quint32,quint32))); QVERIFY(outputManagementAnnouncedSpy.isValid()); QSignalSpy serverSideDecorationManagerAnnouncedSpy(®istry, &Registry::serverSideDecorationManagerAnnounced); QVERIFY(serverSideDecorationManagerAnnouncedSpy.isValid()); QSignalSpy blurAnnouncedSpy(®istry, &Registry::blurAnnounced); QVERIFY(blurAnnouncedSpy.isValid()); + QSignalSpy idleInhibitManagerUnstableV1AnnouncedSpy(®istry, &Registry::idleInhibitManagerUnstableV1Announced); + QVERIFY(idleInhibitManagerUnstableV1AnnouncedSpy.isValid()); QVERIFY(!registry.isValid()); registry.create(connection.display()); registry.setup(); QVERIFY(shmAnnouncedSpy.wait()); QVERIFY(!compositorAnnouncedSpy.isEmpty()); QVERIFY(!outputAnnouncedSpy.isEmpty()); QVERIFY(!outputDeviceAnnouncedSpy.isEmpty()); QVERIFY(!shellAnnouncedSpy.isEmpty()); QVERIFY(!seatAnnouncedSpy.isEmpty()); QVERIFY(!subCompositorAnnouncedSpy.isEmpty()); QVERIFY(!outputManagementAnnouncedSpy.isEmpty()); QVERIFY(!serverSideDecorationManagerAnnouncedSpy.isEmpty()); QVERIFY(!blurAnnouncedSpy.isEmpty()); + QVERIFY(!idleInhibitManagerUnstableV1AnnouncedSpy.isEmpty()); QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::Compositor)); QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::Output)); QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::OutputDevice)); QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::Seat)); QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::Shell)); QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::Shm)); QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::SubCompositor)); QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::FullscreenShell)); QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::OutputManagement)); QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::ServerSideDecorationManager)); QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::Blur)); + QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::IdleInhibitManagerUnstableV1)); QVERIFY(!registry.interfaces(KWayland::Client::Registry::Interface::Compositor).isEmpty()); QVERIFY(!registry.interfaces(KWayland::Client::Registry::Interface::Output).isEmpty()); QVERIFY(!registry.interfaces(KWayland::Client::Registry::Interface::OutputDevice).isEmpty()); QVERIFY(!registry.interfaces(KWayland::Client::Registry::Interface::Seat).isEmpty()); QVERIFY(!registry.interfaces(KWayland::Client::Registry::Interface::Shell).isEmpty()); QVERIFY(!registry.interfaces(KWayland::Client::Registry::Interface::Shm).isEmpty()); QVERIFY(!registry.interfaces(KWayland::Client::Registry::Interface::SubCompositor).isEmpty()); QVERIFY(registry.interfaces(KWayland::Client::Registry::Interface::FullscreenShell).isEmpty()); QVERIFY(!registry.interfaces(KWayland::Client::Registry::Interface::OutputManagement).isEmpty()); QVERIFY(!registry.interfaces(KWayland::Client::Registry::Interface::ServerSideDecorationManager).isEmpty()); QVERIFY(!registry.interfaces(KWayland::Client::Registry::Interface::Blur).isEmpty()); + QVERIFY(!registry.interfaces(KWayland::Client::Registry::Interface::IdleInhibitManagerUnstableV1).isEmpty()); QSignalSpy seatRemovedSpy(®istry, SIGNAL(seatRemoved(quint32))); QVERIFY(seatRemovedSpy.isValid()); Seat *seat = registry.createSeat(registry.interface(Registry::Interface::Seat).name, registry.interface(Registry::Interface::Seat).version, ®istry); Shell *shell = registry.createShell(registry.interface(Registry::Interface::Shell).name, registry.interface(Registry::Interface::Shell).version, ®istry); Output *output = registry.createOutput(registry.interface(Registry::Interface::Output).name, registry.interface(Registry::Interface::Output).version, ®istry); Compositor *compositor = registry.createCompositor(registry.interface(Registry::Interface::Compositor).name, registry.interface(Registry::Interface::Compositor).version, ®istry); SubCompositor *subcompositor = registry.createSubCompositor(registry.interface(Registry::Interface::SubCompositor).name, registry.interface(Registry::Interface::SubCompositor).version, ®istry); ServerSideDecorationManager *serverSideDeco = registry.createServerSideDecorationManager(registry.interface(Registry::Interface::ServerSideDecorationManager).name, registry.interface(Registry::Interface::ServerSideDecorationManager).version, ®istry); BlurManager *blurManager = registry.createBlurManager(registry.interface(Registry::Interface::Blur).name, registry.interface(Registry::Interface::Blur).version, ®istry); + auto idleInhibitManager = registry.createIdleInhibitManager(registry.interface(Registry::Interface::IdleInhibitManagerUnstableV1).name, registry.interface(Registry::Interface::IdleInhibitManagerUnstableV1).version, ®istry); connection.flush(); m_display->dispatchEvents(); QSignalSpy seatObjectRemovedSpy(seat, &Seat::removed); QVERIFY(seatObjectRemovedSpy.isValid()); QSignalSpy shellObjectRemovedSpy(shell, &Shell::removed); QVERIFY(shellObjectRemovedSpy.isValid()); QSignalSpy outputObjectRemovedSpy(output, &Output::removed); QVERIFY(outputObjectRemovedSpy.isValid()); QSignalSpy compositorObjectRemovedSpy(compositor, &Compositor::removed); QVERIFY(compositorObjectRemovedSpy.isValid()); QSignalSpy subcompositorObjectRemovedSpy(subcompositor, &SubCompositor::removed); QVERIFY(subcompositorObjectRemovedSpy.isValid()); + QSignalSpy idleInhibitManagerObjectRemovedSpy(idleInhibitManager, &IdleInhibitManager::removed); + QVERIFY(idleInhibitManagerObjectRemovedSpy.isValid()); delete m_seat; QVERIFY(seatRemovedSpy.wait()); QCOMPARE(seatRemovedSpy.first().first(), seatAnnouncedSpy.first().first()); QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::Seat)); QVERIFY(registry.interfaces(KWayland::Client::Registry::Interface::Seat).isEmpty()); QCOMPARE(seatObjectRemovedSpy.count(), 1); QSignalSpy shellRemovedSpy(®istry, SIGNAL(shellRemoved(quint32))); QVERIFY(shellRemovedSpy.isValid()); delete m_shell; QVERIFY(shellRemovedSpy.wait()); QCOMPARE(shellRemovedSpy.first().first(), shellAnnouncedSpy.first().first()); QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::Shell)); QVERIFY(registry.interfaces(KWayland::Client::Registry::Interface::Shell).isEmpty()); QCOMPARE(shellObjectRemovedSpy.count(), 1); QSignalSpy outputRemovedSpy(®istry, SIGNAL(outputRemoved(quint32))); QVERIFY(outputRemovedSpy.isValid()); delete m_output; QVERIFY(outputRemovedSpy.wait()); QCOMPARE(outputRemovedSpy.first().first(), outputAnnouncedSpy.first().first()); QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::Output)); QVERIFY(registry.interfaces(KWayland::Client::Registry::Interface::Output).isEmpty()); QCOMPARE(outputObjectRemovedSpy.count(), 1); QSignalSpy outputDeviceRemovedSpy(®istry, SIGNAL(outputDeviceRemoved(quint32))); QVERIFY(outputDeviceRemovedSpy.isValid()); delete m_outputDevice; QVERIFY(outputDeviceRemovedSpy.wait()); QCOMPARE(outputDeviceRemovedSpy.first().first(), outputDeviceAnnouncedSpy.first().first()); QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::OutputDevice)); QVERIFY(registry.interfaces(KWayland::Client::Registry::Interface::OutputDevice).isEmpty()); QSignalSpy compositorRemovedSpy(®istry, SIGNAL(compositorRemoved(quint32))); QVERIFY(compositorRemovedSpy.isValid()); delete m_compositor; QVERIFY(compositorRemovedSpy.wait()); QCOMPARE(compositorRemovedSpy.first().first(), compositorAnnouncedSpy.first().first()); QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::Compositor)); QVERIFY(registry.interfaces(KWayland::Client::Registry::Interface::Compositor).isEmpty()); QCOMPARE(compositorObjectRemovedSpy.count(), 1); QSignalSpy subCompositorRemovedSpy(®istry, SIGNAL(subCompositorRemoved(quint32))); QVERIFY(subCompositorRemovedSpy.isValid()); delete m_subcompositor; QVERIFY(subCompositorRemovedSpy.wait()); QCOMPARE(subCompositorRemovedSpy.first().first(), subCompositorAnnouncedSpy.first().first()); QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::SubCompositor)); QVERIFY(registry.interfaces(KWayland::Client::Registry::Interface::SubCompositor).isEmpty()); QCOMPARE(subcompositorObjectRemovedSpy.count(), 1); QSignalSpy outputManagementRemovedSpy(®istry, SIGNAL(outputManagementRemoved(quint32))); QVERIFY(outputManagementRemovedSpy.isValid()); delete m_outputManagement; QVERIFY(outputManagementRemovedSpy.wait()); QCOMPARE(outputManagementRemovedSpy.first().first(), outputManagementAnnouncedSpy.first().first()); QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::OutputManagement)); QVERIFY(registry.interfaces(KWayland::Client::Registry::Interface::OutputManagement).isEmpty()); QSignalSpy serverSideDecoManagerRemovedSpy(®istry, &Registry::serverSideDecorationManagerRemoved); QVERIFY(serverSideDecoManagerRemovedSpy.isValid()); QSignalSpy serverSideDecoManagerObjectRemovedSpy(serverSideDeco, &ServerSideDecorationManager::removed); QVERIFY(serverSideDecoManagerObjectRemovedSpy.isValid()); delete m_serverSideDecorationManager; QVERIFY(serverSideDecoManagerRemovedSpy.wait()); QCOMPARE(serverSideDecoManagerRemovedSpy.first().first(), serverSideDecorationManagerAnnouncedSpy.first().first()); QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::ServerSideDecorationManager)); QVERIFY(registry.interfaces(KWayland::Client::Registry::Interface::ServerSideDecorationManager).isEmpty()); QCOMPARE(serverSideDecoManagerObjectRemovedSpy.count(), 1); QSignalSpy blurRemovedSpy(®istry, &Registry::blurRemoved); QVERIFY(blurRemovedSpy.isValid()); QSignalSpy blurObjectRemovedSpy(blurManager, &BlurManager::removed); QVERIFY(blurObjectRemovedSpy.isValid()); delete m_blur; QVERIFY(blurRemovedSpy.wait()); QCOMPARE(blurRemovedSpy.first().first(), blurRemovedSpy.first().first()); QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::Blur)); QVERIFY(registry.interfaces(KWayland::Client::Registry::Interface::Blur).isEmpty()); QCOMPARE(blurObjectRemovedSpy.count(), 1); + QSignalSpy idleInhibitManagerUnstableV1RemovedSpy(®istry, &Registry::idleInhibitManagerUnstableV1Removed); + QVERIFY(idleInhibitManagerUnstableV1RemovedSpy.isValid()); + delete m_idleInhibit; + QVERIFY(idleInhibitManagerUnstableV1RemovedSpy.wait()); + QCOMPARE(idleInhibitManagerUnstableV1RemovedSpy.first().first(), idleInhibitManagerUnstableV1AnnouncedSpy.first().first()); + QVERIFY(registry.interfaces(KWayland::Client::Registry::Interface::IdleInhibitManagerUnstableV1).isEmpty()); + QCOMPARE(idleInhibitManagerObjectRemovedSpy.count(), 1); + // cannot test shmRemoved as there is no functionality for it // verify everything has been removed only once QCOMPARE(seatObjectRemovedSpy.count(), 1); QCOMPARE(shellObjectRemovedSpy.count(), 1); QCOMPARE(outputObjectRemovedSpy.count(), 1); QCOMPARE(compositorObjectRemovedSpy.count(), 1); QCOMPARE(subcompositorObjectRemovedSpy.count(), 1); QCOMPARE(serverSideDecoManagerObjectRemovedSpy.count(), 1); QCOMPARE(blurObjectRemovedSpy.count(), 1); + QCOMPARE(idleInhibitManagerObjectRemovedSpy.count(), 1); } void TestWaylandRegistry::testOutOfSyncRemoval() { //This tests the following sequence of events //server announces global //client binds to that global //server removes the global //(simultaneously) the client legimitely uses the bound resource to the global //client then gets the server events...it should no-op, not be a protcol error using namespace KWayland::Client; KWayland::Client::ConnectionThread connection; QSignalSpy connectedSpy(&connection, SIGNAL(connected())); connection.setSocketName(s_socketName); connection.initConnection(); QVERIFY(connectedSpy.wait()); connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, &connection, [&connection] { wl_display_flush(connection.display()); } ); KWayland::Client::Registry registry; QVERIFY(!registry.isValid()); registry.create(connection.display()); registry.setup(); QSignalSpy blurAnnouncedSpy(®istry, &Registry::blurAnnounced); QSignalSpy contrastAnnouncedSpy(®istry, &Registry::blurAnnounced); blurAnnouncedSpy.wait(); contrastAnnouncedSpy.wait(); BlurManager *blurManager = registry.createBlurManager(registry.interface(Registry::Interface::Blur).name, registry.interface(Registry::Interface::Blur).version, ®istry); ContrastManager *contrastManager = registry.createContrastManager(registry.interface(Registry::Interface::Contrast).name, registry.interface(Registry::Interface::Contrast).version, ®istry); connection.flush(); m_display->dispatchEvents(); QScopedPointer compositor(registry.createCompositor(registry.interface(Registry::Interface::Compositor).name, registry.interface(Registry::Interface::Compositor).version)); QScopedPointer surface(compositor->createSurface()); QVERIFY(surface); //remove blur QSignalSpy blurRemovedSpy(®istry, &Registry::blurRemoved); delete m_blur; //client hasn't processed the event yet QVERIFY(blurRemovedSpy.count() == 0); //use the in the client blurManager->createBlur(surface.data(), 0); //now process events, QVERIFY(blurRemovedSpy.wait()); QVERIFY(blurRemovedSpy.count() == 1); //remove background contrast QSignalSpy contrastRemovedSpy(®istry, &Registry::contrastRemoved); delete m_contrast; //client hasn't processed the event yet QVERIFY(contrastRemovedSpy.count() == 0); //use the in the client contrastManager->createContrast(surface.data(), 0); //now process events, QVERIFY(contrastRemovedSpy.wait()); QVERIFY(contrastRemovedSpy.count() == 1); } void TestWaylandRegistry::testDestroy() { using namespace KWayland::Client; KWayland::Client::ConnectionThread connection; QSignalSpy connectedSpy(&connection, SIGNAL(connected())); connection.setSocketName(s_socketName); connection.initConnection(); QVERIFY(connectedSpy.wait()); Registry registry; QSignalSpy shellAnnouncedSpy(®istry, SIGNAL(shellAnnounced(quint32,quint32))); QVERIFY(!registry.isValid()); registry.create(&connection); registry.setup(); QVERIFY(registry.isValid()); //create some arbitrary Interface shellAnnouncedSpy.wait(); QScopedPointer shell(registry.createShell(registry.interface(Registry::Interface::Shell).name, registry.interface(Registry::Interface::Shell).version, ®istry)); QSignalSpy connectionDiedSpy(&connection, SIGNAL(connectionDied())); QSignalSpy registryDiedSpy(®istry, SIGNAL(registryDestroyed())); QVERIFY(connectionDiedSpy.isValid()); QVERIFY(registryDiedSpy.isValid()); delete m_display; m_display = nullptr; QVERIFY(connectionDiedSpy.wait()); QVERIFY(connectionDiedSpy.count() == 1); QVERIFY(registryDiedSpy.count() == 1); // now the registry should be destroyed; QVERIFY(!registry.isValid()); // calling destroy again should not fail registry.destroy(); shell->destroy(); } void TestWaylandRegistry::testGlobalSync() { using namespace KWayland::Client; ConnectionThread connection; connection.setSocketName(s_socketName); QSignalSpy connectedSpy(&connection, SIGNAL(connected())); connection.initConnection(); QVERIFY(connectedSpy.wait()); Registry registry; QSignalSpy syncSpy(®istry, SIGNAL(interfacesAnnounced())); // Most simple case: don't even use the ConnectionThread, // just its display. registry.create(connection.display()); registry.setup(); QVERIFY(syncSpy.wait()); QCOMPARE(syncSpy.count(), 1); registry.destroy(); } void TestWaylandRegistry::testGlobalSyncThreaded() { // More complex case, use a ConnectionThread, in a different Thread, // and our own EventQueue using namespace KWayland::Client; ConnectionThread connection; connection.setSocketName(s_socketName); QThread thread; connection.moveToThread(&thread); thread.start(); QSignalSpy connectedSpy(&connection, SIGNAL(connected())); connection.initConnection(); QVERIFY(connectedSpy.wait()); EventQueue queue; queue.setup(&connection); Registry registry; QSignalSpy syncSpy(®istry, SIGNAL(interfacesAnnounced())); registry.setEventQueue(&queue); registry.create(&connection); registry.setup(); QVERIFY(syncSpy.wait()); QCOMPARE(syncSpy.count(), 1); registry.destroy(); thread.quit(); thread.wait(); } void TestWaylandRegistry::testAnnounceMultiple() { using namespace KWayland::Client; ConnectionThread connection; connection.setSocketName(s_socketName); QSignalSpy connectedSpy(&connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); connection.initConnection(); QVERIFY(connectedSpy.wait()); connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, &connection, [&connection] { wl_display_flush(connection.display()); } ); Registry registry; QSignalSpy syncSpy(®istry, &Registry::interfacesAnnounced); QVERIFY(syncSpy.isValid()); // Most simple case: don't even use the ConnectionThread, // just its display. registry.create(connection.display()); registry.setup(); QVERIFY(syncSpy.wait()); QCOMPARE(syncSpy.count(), 1); // we should have one output now QCOMPARE(registry.interfaces(Registry::Interface::Output).count(), 1); QSignalSpy outputAnnouncedSpy(®istry, &Registry::outputAnnounced); QVERIFY(outputAnnouncedSpy.isValid()); m_display->createOutput()->create(); QVERIFY(outputAnnouncedSpy.wait()); QCOMPARE(registry.interfaces(Registry::Interface::Output).count(), 2); QCOMPARE(registry.interfaces(Registry::Interface::Output).last().name, outputAnnouncedSpy.first().first().value()); QCOMPARE(registry.interfaces(Registry::Interface::Output).last().version, outputAnnouncedSpy.first().last().value()); QCOMPARE(registry.interface(Registry::Interface::Output).name, outputAnnouncedSpy.first().first().value()); QCOMPARE(registry.interface(Registry::Interface::Output).version, outputAnnouncedSpy.first().last().value()); auto output = m_display->createOutput(); output->create(); QVERIFY(outputAnnouncedSpy.wait()); QCOMPARE(registry.interfaces(Registry::Interface::Output).count(), 3); QCOMPARE(registry.interfaces(Registry::Interface::Output).last().name, outputAnnouncedSpy.last().first().value()); QCOMPARE(registry.interfaces(Registry::Interface::Output).last().version, outputAnnouncedSpy.last().last().value()); QCOMPARE(registry.interface(Registry::Interface::Output).name, outputAnnouncedSpy.last().first().value()); QCOMPARE(registry.interface(Registry::Interface::Output).version, outputAnnouncedSpy.last().last().value()); QSignalSpy outputRemovedSpy(®istry, &Registry::outputRemoved); QVERIFY(outputRemovedSpy.isValid()); delete output; QVERIFY(outputRemovedSpy.wait()); QCOMPARE(outputRemovedSpy.first().first().value(), outputAnnouncedSpy.last().first().value()); QCOMPARE(registry.interfaces(Registry::Interface::Output).count(), 2); QCOMPARE(registry.interfaces(Registry::Interface::Output).last().name, outputAnnouncedSpy.first().first().value()); QCOMPARE(registry.interfaces(Registry::Interface::Output).last().version, outputAnnouncedSpy.first().last().value()); QCOMPARE(registry.interface(Registry::Interface::Output).name, outputAnnouncedSpy.first().first().value()); QCOMPARE(registry.interface(Registry::Interface::Output).version, outputAnnouncedSpy.first().last().value()); } void TestWaylandRegistry::testAnnounceMultipleOutputDevices() { using namespace KWayland::Client; ConnectionThread connection; connection.setSocketName(s_socketName); QSignalSpy connectedSpy(&connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); connection.initConnection(); QVERIFY(connectedSpy.wait()); connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, &connection, [&connection] { wl_display_flush(connection.display()); } ); Registry registry; QSignalSpy syncSpy(®istry, &Registry::interfacesAnnounced); QVERIFY(syncSpy.isValid()); // Most simple case: don't even use the ConnectionThread, // just its display. registry.create(connection.display()); registry.setup(); QVERIFY(syncSpy.wait()); QCOMPARE(syncSpy.count(), 1); // we should have one output now QCOMPARE(registry.interfaces(Registry::Interface::OutputDevice).count(), 1); QSignalSpy outputDeviceAnnouncedSpy(®istry, &Registry::outputDeviceAnnounced); QVERIFY(outputDeviceAnnouncedSpy.isValid()); m_display->createOutputDevice()->create(); QVERIFY(outputDeviceAnnouncedSpy.wait()); QCOMPARE(registry.interfaces(Registry::Interface::OutputDevice).count(), 2); QCOMPARE(registry.interfaces(Registry::Interface::OutputDevice).last().name, outputDeviceAnnouncedSpy.first().first().value()); QCOMPARE(registry.interfaces(Registry::Interface::OutputDevice).last().version, outputDeviceAnnouncedSpy.first().last().value()); QCOMPARE(registry.interface(Registry::Interface::OutputDevice).name, outputDeviceAnnouncedSpy.first().first().value()); QCOMPARE(registry.interface(Registry::Interface::OutputDevice).version, outputDeviceAnnouncedSpy.first().last().value()); auto outputDevice = m_display->createOutputDevice(); outputDevice->create(); QVERIFY(outputDeviceAnnouncedSpy.wait()); QCOMPARE(registry.interfaces(Registry::Interface::OutputDevice).count(), 3); QCOMPARE(registry.interfaces(Registry::Interface::OutputDevice).last().name, outputDeviceAnnouncedSpy.last().first().value()); QCOMPARE(registry.interfaces(Registry::Interface::OutputDevice).last().version, outputDeviceAnnouncedSpy.last().last().value()); QCOMPARE(registry.interface(Registry::Interface::OutputDevice).name, outputDeviceAnnouncedSpy.last().first().value()); QCOMPARE(registry.interface(Registry::Interface::OutputDevice).version, outputDeviceAnnouncedSpy.last().last().value()); QSignalSpy outputDeviceRemovedSpy(®istry, &Registry::outputDeviceRemoved); QVERIFY(outputDeviceRemovedSpy.isValid()); delete outputDevice; QVERIFY(outputDeviceRemovedSpy.wait()); QCOMPARE(outputDeviceRemovedSpy.first().first().value(), outputDeviceAnnouncedSpy.last().first().value()); QCOMPARE(registry.interfaces(Registry::Interface::OutputDevice).count(), 2); QCOMPARE(registry.interfaces(Registry::Interface::OutputDevice).last().name, outputDeviceAnnouncedSpy.first().first().value()); QCOMPARE(registry.interfaces(Registry::Interface::OutputDevice).last().version, outputDeviceAnnouncedSpy.first().last().value()); QCOMPARE(registry.interface(Registry::Interface::OutputDevice).name, outputDeviceAnnouncedSpy.first().first().value()); QCOMPARE(registry.interface(Registry::Interface::OutputDevice).version, outputDeviceAnnouncedSpy.first().last().value()); } QTEST_GUILESS_MAIN(TestWaylandRegistry) #include "test_wayland_registry.moc" diff --git a/autotests/client/test_wayland_surface.cpp b/autotests/client/test_wayland_surface.cpp index db24b64..e106f4c 100644 --- a/autotests/client/test_wayland_surface.cpp +++ b/autotests/client/test_wayland_surface.cpp @@ -1,1095 +1,1163 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ // Qt #include #include #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" +#include "../../src/client/idleinhibit.h" #include "../../src/client/output.h" #include "../../src/client/surface.h" #include "../../src/client/region.h" #include "../../src/client/registry.h" #include "../../src/client/shm_pool.h" #include "../../src/server/buffer_interface.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/display.h" +#include "../../src/server/idleinhibit_interface.h" #include "../../src/server/surface_interface.h" // Wayland #include +using KWayland::Client::Registry; + class TestWaylandSurface : public QObject { Q_OBJECT public: explicit TestWaylandSurface(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testStaticAccessor(); void testDamage(); void testFrameCallback(); void testAttachBuffer(); void testMultipleSurfaces(); void testOpaque(); void testInput(); void testScale(); void testDestroy(); void testUnmapOfNotMappedSurface(); void testDamageTracking(); void testSurfaceAt(); void testDestroyAttachedBuffer(); void testDestroyWithPendingCallback(); void testOutput(); void testDisconnect(); + void testInhibit(); private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; + KWayland::Server::IdleInhibitManagerInterface *m_idleInhibitInterface = nullptr; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::ShmPool *m_shm; KWayland::Client::EventQueue *m_queue; + KWayland::Client::IdleInhibitManager *m_idleInhibitManager = nullptr; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwin-test-wayland-surface-0"); TestWaylandSurface::TestWaylandSurface(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_thread(nullptr) { } void TestWaylandSurface::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_display->createShm(); m_compositorInterface = m_display->createCompositor(m_display); QVERIFY(m_compositorInterface); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); + m_idleInhibitInterface = m_display->createIdleInhibitManager(IdleInhibitManagerInterfaceVersion::UnstableV1, m_display); + QVERIFY(m_idleInhibitInterface); + m_idleInhibitInterface->create(); + QVERIFY(m_idleInhibitInterface->isValid()); + // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, SIGNAL(connected())); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); /*connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, m_connection, [this]() { if (m_connection->display()) { wl_display_flush(m_connection->display()); } } );*/ m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); KWayland::Client::Registry registry; registry.setEventQueue(m_queue); QSignalSpy compositorSpy(®istry, SIGNAL(compositorAnnounced(quint32,quint32))); QSignalSpy shmSpy(®istry, SIGNAL(shmAnnounced(quint32,quint32))); QSignalSpy allAnnounced(®istry, SIGNAL(interfacesAnnounced())); QVERIFY(allAnnounced.isValid()); QVERIFY(shmSpy.isValid()); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(allAnnounced.wait()); QVERIFY(!compositorSpy.isEmpty()); QVERIFY(!shmSpy.isEmpty()); m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); QVERIFY(m_compositor->isValid()); m_shm = registry.createShmPool(shmSpy.first().first().value(), shmSpy.first().last().value(), this); QVERIFY(m_shm->isValid()); + + m_idleInhibitManager = registry.createIdleInhibitManager(registry.interface(Registry::Interface::IdleInhibitManagerUnstableV1).name, registry.interface(Registry::Interface::IdleInhibitManagerUnstableV1).version, this); + QVERIFY(m_idleInhibitManager->isValid()); } void TestWaylandSurface::cleanup() { if (m_compositor) { delete m_compositor; m_compositor = nullptr; } + if (m_idleInhibitManager) { + delete m_idleInhibitManager; + m_idleInhibitManager = nullptr; + } if (m_shm) { delete m_shm; m_shm = nullptr; } if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_compositorInterface; m_compositorInterface = nullptr; delete m_display; m_display = nullptr; } void TestWaylandSurface::testStaticAccessor() { QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); QVERIFY(!KWayland::Server::SurfaceInterface::get(nullptr)); QVERIFY(!KWayland::Server::SurfaceInterface::get(1, nullptr)); QVERIFY(KWayland::Client::Surface::all().isEmpty()); KWayland::Client::Surface *s1 = m_compositor->createSurface(); QVERIFY(s1->isValid()); QCOMPARE(KWayland::Client::Surface::all().count(), 1); QCOMPARE(KWayland::Client::Surface::all().first(), s1); QCOMPARE(KWayland::Client::Surface::get(*s1), s1); QVERIFY(serverSurfaceCreated.wait()); auto serverSurface1 = serverSurfaceCreated.first().first().value(); QVERIFY(serverSurface1); QCOMPARE(KWayland::Server::SurfaceInterface::get(serverSurface1->resource()), serverSurface1); QCOMPARE(KWayland::Server::SurfaceInterface::get(serverSurface1->id(), serverSurface1->client()), serverSurface1); QVERIFY(!s1->size().isValid()); QSignalSpy sizeChangedSpy(s1, SIGNAL(sizeChanged(QSize))); QVERIFY(sizeChangedSpy.isValid()); const QSize testSize(200, 300); s1->setSize(testSize); QCOMPARE(s1->size(), testSize); QCOMPARE(sizeChangedSpy.count(), 1); QCOMPARE(sizeChangedSpy.first().first().toSize(), testSize); // add another surface KWayland::Client::Surface *s2 = m_compositor->createSurface(); QVERIFY(s2->isValid()); QCOMPARE(KWayland::Client::Surface::all().count(), 2); QCOMPARE(KWayland::Client::Surface::all().first(), s1); QCOMPARE(KWayland::Client::Surface::all().last(), s2); QCOMPARE(KWayland::Client::Surface::get(*s1), s1); QCOMPARE(KWayland::Client::Surface::get(*s2), s2); serverSurfaceCreated.clear(); QVERIFY(serverSurfaceCreated.wait()); auto serverSurface2 = serverSurfaceCreated.first().first().value(); QVERIFY(serverSurface2); QCOMPARE(KWayland::Server::SurfaceInterface::get(serverSurface1->resource()), serverSurface1); QCOMPARE(KWayland::Server::SurfaceInterface::get(serverSurface1->id(), serverSurface1->client()), serverSurface1); QCOMPARE(KWayland::Server::SurfaceInterface::get(serverSurface2->resource()), serverSurface2); QCOMPARE(KWayland::Server::SurfaceInterface::get(serverSurface2->id(), serverSurface2->client()), serverSurface2); // delete s2 again delete s2; QCOMPARE(KWayland::Client::Surface::all().count(), 1); QCOMPARE(KWayland::Client::Surface::all().first(), s1); QCOMPARE(KWayland::Client::Surface::get(*s1), s1); // and finally delete the last one delete s1; QVERIFY(KWayland::Client::Surface::all().isEmpty()); QVERIFY(!KWayland::Client::Surface::get(nullptr)); QSignalSpy unboundSpy(serverSurface1, &KWayland::Server::Resource::unbound); QVERIFY(unboundSpy.isValid()); QVERIFY(unboundSpy.wait()); QVERIFY(!KWayland::Server::SurfaceInterface::get(nullptr)); QVERIFY(!KWayland::Server::SurfaceInterface::get(1, nullptr)); } void TestWaylandSurface::testDamage() { QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); KWayland::Client::Surface *s = m_compositor->createSurface(); QVERIFY(serverSurfaceCreated.wait()); KWayland::Server::SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); QVERIFY(serverSurface); QCOMPARE(serverSurface->damage(), QRegion()); QVERIFY(serverSurface->parentResource()); QVERIFY(!serverSurface->isMapped()); QSignalSpy damageSpy(serverSurface, SIGNAL(damaged(QRegion))); QVERIFY(damageSpy.isValid()); // send damage without a buffer s->damage(QRect(0, 0, 100, 100)); s->commit(KWayland::Client::Surface::CommitFlag::None); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCoreApplication::processEvents(); QVERIFY(damageSpy.isEmpty()); QVERIFY(!serverSurface->isMapped()); QImage img(QSize(10, 10), QImage::Format_ARGB32_Premultiplied); img.fill(Qt::black); auto b = m_shm->createBuffer(img); s->attachBuffer(b); s->damage(QRect(0, 0, 10, 10)); s->commit(KWayland::Client::Surface::CommitFlag::None); QVERIFY(damageSpy.wait()); QCOMPARE(serverSurface->damage(), QRegion(0, 0, 10, 10)); QCOMPARE(damageSpy.first().first().value(), QRegion(0, 0, 10, 10)); QVERIFY(serverSurface->isMapped()); // damage multiple times QRegion testRegion(5, 8, 3, 6); testRegion = testRegion.united(QRect(10, 20, 30, 15)); img = QImage(QSize(40, 35), QImage::Format_ARGB32_Premultiplied); img.fill(Qt::black); b = m_shm->createBuffer(img); s->attachBuffer(b); s->damage(testRegion); damageSpy.clear(); s->commit(KWayland::Client::Surface::CommitFlag::None); QVERIFY(damageSpy.wait()); QCOMPARE(serverSurface->damage(), testRegion); QCOMPARE(damageSpy.first().first().value(), testRegion); QVERIFY(serverSurface->isMapped()); } void TestWaylandSurface::testFrameCallback() { QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); KWayland::Client::Surface *s = m_compositor->createSurface(); QVERIFY(serverSurfaceCreated.wait()); KWayland::Server::SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); QVERIFY(serverSurface); QSignalSpy damageSpy(serverSurface, SIGNAL(damaged(QRegion))); QVERIFY(damageSpy.isValid()); QSignalSpy frameRenderedSpy(s, SIGNAL(frameRendered())); QVERIFY(frameRenderedSpy.isValid()); QImage img(QSize(10, 10), QImage::Format_ARGB32_Premultiplied); img.fill(Qt::black); auto b = m_shm->createBuffer(img); s->attachBuffer(b); s->damage(QRect(0, 0, 10, 10)); s->commit(); QVERIFY(damageSpy.wait()); serverSurface->frameRendered(10); QVERIFY(frameRenderedSpy.isEmpty()); QVERIFY(frameRenderedSpy.wait()); QVERIFY(!frameRenderedSpy.isEmpty()); } void TestWaylandSurface::testAttachBuffer() { // create the surface QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); KWayland::Client::Surface *s = m_compositor->createSurface(); QVERIFY(serverSurfaceCreated.wait()); KWayland::Server::SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); QVERIFY(serverSurface); // create three images QImage black(24, 24, QImage::Format_RGB32); black.fill(Qt::black); QImage red(24, 24, QImage::Format_ARGB32); //Note - deliberately not premultiplied red.fill(QColor(255, 0, 0, 128)); QImage blue(24, 24, QImage::Format_ARGB32_Premultiplied); blue.fill(QColor(0, 0, 255, 128)); wl_buffer *blackBuffer = *(m_shm->createBuffer(black).data()); auto redBuffer = m_shm->createBuffer(red); auto blueBuffer = m_shm->createBuffer(blue).toStrongRef(); QCOMPARE(blueBuffer->format(), KWayland::Client::Buffer::Format::ARGB32); QCOMPARE(blueBuffer->size(), blue.size()); QVERIFY(!blueBuffer->isReleased()); QVERIFY(!blueBuffer->isUsed()); QCOMPARE(blueBuffer->stride(), blue.bytesPerLine()); s->attachBuffer(redBuffer.data()); s->attachBuffer(blackBuffer); s->damage(QRect(0, 0, 24, 24)); s->commit(KWayland::Client::Surface::CommitFlag::None); QSignalSpy damageSpy(serverSurface, SIGNAL(damaged(QRegion))); QVERIFY(damageSpy.isValid()); QSignalSpy unmappedSpy(serverSurface, SIGNAL(unmapped())); QVERIFY(unmappedSpy.isValid()); QVERIFY(damageSpy.wait()); QVERIFY(unmappedSpy.isEmpty()); // now the ServerSurface should have the black image attached as a buffer KWayland::Server::BufferInterface *buffer = serverSurface->buffer(); buffer->ref(); QVERIFY(buffer->shmBuffer()); QCOMPARE(buffer->data(), black); QCOMPARE(buffer->data().format(), QImage::Format_RGB32); // render another frame s->attachBuffer(redBuffer); s->damage(QRect(0, 0, 24, 24)); s->commit(KWayland::Client::Surface::CommitFlag::None); damageSpy.clear(); QVERIFY(damageSpy.wait()); QVERIFY(unmappedSpy.isEmpty()); KWayland::Server::BufferInterface *buffer2 = serverSurface->buffer(); buffer2->ref(); QVERIFY(buffer2->shmBuffer()); QCOMPARE(buffer2->data().format(), QImage::Format_ARGB32_Premultiplied); QCOMPARE(buffer2->data().width(), 24); QCOMPARE(buffer2->data().height(), 24); for (int i = 0; i < 24; ++i) { for (int j = 0; j < 24; ++j) { // it's premultiplied in the format QCOMPARE(buffer2->data().pixel(i, j), qRgba(128, 0, 0, 128)); } } buffer2->unref(); QVERIFY(buffer2->isReferenced()); QVERIFY(!redBuffer.data()->isReleased()); // render another frame blueBuffer->setUsed(true); QVERIFY(blueBuffer->isUsed()); s->attachBuffer(blueBuffer.data()); s->damage(QRect(0, 0, 24, 24)); QSignalSpy frameRenderedSpy(s, SIGNAL(frameRendered())); QVERIFY(frameRenderedSpy.isValid()); s->commit(); damageSpy.clear(); QVERIFY(damageSpy.wait()); QVERIFY(unmappedSpy.isEmpty()); QVERIFY(!buffer2->isReferenced()); delete buffer2; // TODO: we should have a signal on when the Buffer gets released QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents); if (!redBuffer.data()->isReleased()) { QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents); } QVERIFY(redBuffer.data()->isReleased()); KWayland::Server::BufferInterface *buffer3 = serverSurface->buffer(); buffer3->ref(); QVERIFY(buffer3->shmBuffer()); QCOMPARE(buffer3->data().format(), QImage::Format_ARGB32_Premultiplied); QCOMPARE(buffer3->data().width(), 24); QCOMPARE(buffer3->data().height(), 24); for (int i = 0; i < 24; ++i) { for (int j = 0; j < 24; ++j) { // it's premultiplied in the format QCOMPARE(buffer3->data().pixel(i, j), qRgba(0, 0, 128, 128)); } } buffer3->unref(); QVERIFY(buffer3->isReferenced()); serverSurface->frameRendered(1); QVERIFY(frameRenderedSpy.wait()); // commit a different value shouldn't change our buffer QCOMPARE(serverSurface->buffer(), buffer3); QVERIFY(serverSurface->input().isNull()); damageSpy.clear(); s->setInputRegion(m_compositor->createRegion(QRegion(0, 0, 24, 24)).get()); s->commit(KWayland::Client::Surface::CommitFlag::None); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCoreApplication::processEvents(); QCOMPARE(serverSurface->input(), QRegion(0, 0, 24, 24)); QCOMPARE(serverSurface->buffer(), buffer3); QVERIFY(damageSpy.isEmpty()); QVERIFY(unmappedSpy.isEmpty()); QVERIFY(serverSurface->isMapped()); // clear the surface s->attachBuffer(blackBuffer); s->damage(QRect(0, 0, 1, 1)); // TODO: better method s->attachBuffer((wl_buffer*)nullptr); s->damage(QRect(0, 0, 10, 10)); s->commit(KWayland::Client::Surface::CommitFlag::None); QVERIFY(unmappedSpy.wait()); QVERIFY(!unmappedSpy.isEmpty()); QCOMPARE(unmappedSpy.count(), 1); QVERIFY(damageSpy.isEmpty()); QVERIFY(!serverSurface->isMapped()); // TODO: add signal test on release buffer->unref(); } void TestWaylandSurface::testMultipleSurfaces() { using namespace KWayland::Client; using namespace KWayland::Server; Registry registry; QSignalSpy shmSpy(®istry, SIGNAL(shmAnnounced(quint32,quint32))); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(shmSpy.wait()); ShmPool pool1; ShmPool pool2; pool1.setup(registry.bindShm(shmSpy.first().first().value(), shmSpy.first().last().value())); pool2.setup(registry.bindShm(shmSpy.first().first().value(), shmSpy.first().last().value())); QVERIFY(pool1.isValid()); QVERIFY(pool2.isValid()); // create the surfaces QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer s1(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *serverSurface1 = serverSurfaceCreated.first().first().value(); QVERIFY(serverSurface1); //second surface QScopedPointer s2(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *serverSurface2 = serverSurfaceCreated.last().first().value(); QVERIFY(serverSurface2); QVERIFY(serverSurface1->resource() != serverSurface2->resource()); // create two images QImage black(24, 24, QImage::Format_RGB32); black.fill(Qt::black); QImage red(24, 24, QImage::Format_ARGB32_Premultiplied); red.fill(QColor(255, 0, 0, 128)); auto blackBuffer = pool1.createBuffer(black); auto redBuffer = pool2.createBuffer(red); s1->attachBuffer(blackBuffer); s1->damage(QRect(0, 0, 24, 24)); s1->commit(Surface::CommitFlag::None); QSignalSpy damageSpy1(serverSurface1, SIGNAL(damaged(QRegion))); QVERIFY(damageSpy1.isValid()); QVERIFY(damageSpy1.wait()); // now the ServerSurface should have the black image attached as a buffer BufferInterface *buffer1 = serverSurface1->buffer(); QVERIFY(buffer1); QImage buffer1Data = buffer1->data(); QCOMPARE(buffer1Data, black); // accessing the same buffer is OK QImage buffer1Data2 = buffer1->data(); QCOMPARE(buffer1Data2, buffer1Data); buffer1Data = QImage(); QVERIFY(buffer1Data.isNull()); buffer1Data2 = QImage(); QVERIFY(buffer1Data2.isNull()); // attach a buffer for the other surface s2->attachBuffer(redBuffer); s2->damage(QRect(0, 0, 24, 24)); s2->commit(Surface::CommitFlag::None); QSignalSpy damageSpy2(serverSurface2, SIGNAL(damaged(QRegion))); QVERIFY(damageSpy2.isValid()); QVERIFY(damageSpy2.wait()); BufferInterface *buffer2 = serverSurface2->buffer(); QVERIFY(buffer2); QImage buffer2Data = buffer2->data(); QCOMPARE(buffer2Data, red); // while buffer2 is accessed we cannot access buffer1 buffer1Data = buffer1->data(); QVERIFY(buffer1Data.isNull()); // a deep copy can be kept around QImage deepCopy = buffer2Data.copy(); QCOMPARE(deepCopy, red); buffer2Data = QImage(); QVERIFY(buffer2Data.isNull()); QCOMPARE(deepCopy, red); // now that buffer2Data is destroyed we can access buffer1 again buffer1Data = buffer1->data(); QVERIFY(!buffer1Data.isNull()); QCOMPARE(buffer1Data, black); } void TestWaylandSurface::testOpaque() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); Surface *s = m_compositor->createSurface(); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); QVERIFY(serverSurface); QSignalSpy opaqueRegionChangedSpy(serverSurface, SIGNAL(opaqueChanged(QRegion))); QVERIFY(opaqueRegionChangedSpy.isValid()); // by default there should be an empty opaque region QCOMPARE(serverSurface->opaque(), QRegion()); // let's install an opaque region s->setOpaqueRegion(m_compositor->createRegion(QRegion(0, 10, 20, 30)).get()); // the region should only be applied after the surface got committed wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(serverSurface->opaque(), QRegion()); QCOMPARE(opaqueRegionChangedSpy.count(), 0); // so let's commit to get the new region s->commit(Surface::CommitFlag::None); QVERIFY(opaqueRegionChangedSpy.wait()); QCOMPARE(opaqueRegionChangedSpy.count(), 1); QCOMPARE(opaqueRegionChangedSpy.last().first().value(), QRegion(0, 10, 20, 30)); QCOMPARE(serverSurface->opaque(), QRegion(0, 10, 20, 30)); // committing without setting a new region shouldn't change s->commit(Surface::CommitFlag::None); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(opaqueRegionChangedSpy.count(), 1); QCOMPARE(serverSurface->opaque(), QRegion(0, 10, 20, 30)); // let's change the opaque region s->setOpaqueRegion(m_compositor->createRegion(QRegion(10, 20, 30, 40)).get()); s->commit(Surface::CommitFlag::None); QVERIFY(opaqueRegionChangedSpy.wait()); QCOMPARE(opaqueRegionChangedSpy.count(), 2); QCOMPARE(opaqueRegionChangedSpy.last().first().value(), QRegion(10, 20, 30, 40)); QCOMPARE(serverSurface->opaque(), QRegion(10, 20, 30, 40)); // and let's go back to an empty region s->setOpaqueRegion(); s->commit(Surface::CommitFlag::None); QVERIFY(opaqueRegionChangedSpy.wait()); QCOMPARE(opaqueRegionChangedSpy.count(), 3); QCOMPARE(opaqueRegionChangedSpy.last().first().value(), QRegion()); QCOMPARE(serverSurface->opaque(), QRegion()); } void TestWaylandSurface::testInput() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); Surface *s = m_compositor->createSurface(); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); QVERIFY(serverSurface); QSignalSpy inputRegionChangedSpy(serverSurface, SIGNAL(inputChanged(QRegion))); QVERIFY(inputRegionChangedSpy.isValid()); // by default there should be an empty == infinite input region QCOMPARE(serverSurface->input(), QRegion()); QCOMPARE(serverSurface->inputIsInfinite(), true); // let's install an input region s->setInputRegion(m_compositor->createRegion(QRegion(0, 10, 20, 30)).get()); // the region should only be applied after the surface got committed wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(serverSurface->input(), QRegion()); QCOMPARE(serverSurface->inputIsInfinite(), true); QCOMPARE(inputRegionChangedSpy.count(), 0); // so let's commit to get the new region s->commit(Surface::CommitFlag::None); QVERIFY(inputRegionChangedSpy.wait()); QCOMPARE(inputRegionChangedSpy.count(), 1); QCOMPARE(inputRegionChangedSpy.last().first().value(), QRegion(0, 10, 20, 30)); QCOMPARE(serverSurface->input(), QRegion(0, 10, 20, 30)); QCOMPARE(serverSurface->inputIsInfinite(), false); // committing without setting a new region shouldn't change s->commit(Surface::CommitFlag::None); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(inputRegionChangedSpy.count(), 1); QCOMPARE(serverSurface->input(), QRegion(0, 10, 20, 30)); QCOMPARE(serverSurface->inputIsInfinite(), false); // let's change the input region s->setInputRegion(m_compositor->createRegion(QRegion(10, 20, 30, 40)).get()); s->commit(Surface::CommitFlag::None); QVERIFY(inputRegionChangedSpy.wait()); QCOMPARE(inputRegionChangedSpy.count(), 2); QCOMPARE(inputRegionChangedSpy.last().first().value(), QRegion(10, 20, 30, 40)); QCOMPARE(serverSurface->input(), QRegion(10, 20, 30, 40)); QCOMPARE(serverSurface->inputIsInfinite(), false); // and let's go back to an empty region s->setInputRegion(); s->commit(Surface::CommitFlag::None); QVERIFY(inputRegionChangedSpy.wait()); QCOMPARE(inputRegionChangedSpy.count(), 3); QCOMPARE(inputRegionChangedSpy.last().first().value(), QRegion()); QCOMPARE(serverSurface->input(), QRegion()); QCOMPARE(serverSurface->inputIsInfinite(), true); } void TestWaylandSurface::testScale() { // this test verifies that updating the scale factor is correctly passed to the Wayland server using namespace KWayland::Client; using namespace KWayland::Server; // create surface QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer s(m_compositor->createSurface()); QCOMPARE(s->scale(), 1); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); QVERIFY(serverSurface); QCOMPARE(serverSurface->scale(), 1); // let's change the scale factor QSignalSpy scaleChangedSpy(serverSurface, &SurfaceInterface::scaleChanged); //changing the scale implicitly changes the size QSignalSpy sizeChangedSpy(serverSurface, &SurfaceInterface::sizeChanged); QVERIFY(scaleChangedSpy.isValid()); s->setScale(2); QCOMPARE(s->scale(), 2); // needs a commit QVERIFY(!scaleChangedSpy.wait(100)); s->commit(Surface::CommitFlag::None); QVERIFY(scaleChangedSpy.wait()); QCOMPARE(scaleChangedSpy.count(), 1); QCOMPARE(scaleChangedSpy.first().first().toInt(), 2); QCOMPARE(serverSurface->scale(), 2); //even though we've changed the scale, if we don't have a buffer we //don't have a size. If we don't have a size it can't have changed QCOMPARE(sizeChangedSpy.count(), 0); QVERIFY(!serverSurface->size().isValid()); // let's try changing to same factor, should not emit changed on server s->setScale(2); s->commit(Surface::CommitFlag::None); QVERIFY(!scaleChangedSpy.wait(100)); // but changing to a different value should still work s->setScale(4); s->commit(Surface::CommitFlag::None); QVERIFY(scaleChangedSpy.wait()); QCOMPARE(scaleChangedSpy.count(), 2); QCOMPARE(scaleChangedSpy.first().first().toInt(), 2); QCOMPARE(scaleChangedSpy.last().first().toInt(), 4); QCOMPARE(serverSurface->scale(), 4); scaleChangedSpy.clear(); //attach a buffer of 100x100, our scale is 4, so this should be a size of 25x25 QImage red(100, 100, QImage::Format_ARGB32_Premultiplied); red.fill(QColor(255, 0, 0, 128)); auto redBuffer = m_shm->createBuffer(red); s->attachBuffer(redBuffer.data()); s->damage(QRect(0,0, 25,25)); s->commit(Surface::CommitFlag::None); QVERIFY(sizeChangedSpy.wait()); QCOMPARE(sizeChangedSpy.count(), 1); QCOMPARE(serverSurface->size(), QSize(25,25)); sizeChangedSpy.clear(); scaleChangedSpy.clear(); //set the scale to 1, buffer is still 100x100 so size should change to 100x100 s->setScale(1); s->commit(Surface::CommitFlag::None); QVERIFY(sizeChangedSpy.wait()); QCOMPARE(sizeChangedSpy.count(), 1); QCOMPARE(scaleChangedSpy.count(), 1); QCOMPARE(serverSurface->scale(), 1); QCOMPARE(serverSurface->size(), QSize(100,100)); sizeChangedSpy.clear(); scaleChangedSpy.clear(); //set scale and size in one commit, buffer is 50x50 at scale 2 so size should be 25x25 QImage blue(50, 50, QImage::Format_ARGB32_Premultiplied); red.fill(QColor(255, 0, 0, 128)); auto blueBuffer = m_shm->createBuffer(blue); s->attachBuffer(blueBuffer.data()); s->setScale(2); s->commit(Surface::CommitFlag::None); QVERIFY(sizeChangedSpy.wait()); QCOMPARE(sizeChangedSpy.count(), 1); QCOMPARE(scaleChangedSpy.count(), 1); QCOMPARE(serverSurface->scale(), 2); QCOMPARE(serverSurface->size(), QSize(25,25)); } void TestWaylandSurface::testDestroy() { using namespace KWayland::Client; Surface *s = m_compositor->createSurface(); connect(m_connection, &ConnectionThread::connectionDied, s, &Surface::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_compositor, &Compositor::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_shm, &ShmPool::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_queue, &EventQueue::destroy); QVERIFY(s->isValid()); QSignalSpy connectionDiedSpy(m_connection, SIGNAL(connectionDied())); QVERIFY(connectionDiedSpy.isValid()); delete m_display; m_display = nullptr; m_compositorInterface = nullptr; QVERIFY(connectionDiedSpy.wait()); // now the Surface should be destroyed; QVERIFY(!s->isValid()); // calling destroy again should not fail s->destroy(); } void TestWaylandSurface::testUnmapOfNotMappedSurface() { // this test verifies that a surface which doesn't have a buffer attached doesn't trigger the unmapped signal using namespace KWayland::Client; using namespace KWayland::Server; // create surface QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer s(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); QSignalSpy unmappedSpy(serverSurface, &SurfaceInterface::unmapped); QVERIFY(unmappedSpy.isValid()); QSignalSpy scaleChanged(serverSurface, &SurfaceInterface::scaleChanged); // let's map a null buffer and change scale to trigger a signal we can wait for s->attachBuffer(Buffer::Ptr()); s->setScale(2); s->commit(Surface::CommitFlag::None); QVERIFY(scaleChanged.wait()); QVERIFY(unmappedSpy.isEmpty()); } void TestWaylandSurface::testDamageTracking() { // this tests the damage tracking feature using namespace KWayland::Client; using namespace KWayland::Server; // create surface QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer s(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); // before first commit, the tracked damage should be empty QVERIFY(serverSurface->trackedDamage().isEmpty()); // Now let's damage the surface QSignalSpy damagedSpy(serverSurface, &SurfaceInterface::damaged); QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::red); s->attachBuffer(m_shm->createBuffer(image)); s->damage(QRect(0, 0, 100, 100)); s->commit(Surface::CommitFlag::None); QVERIFY(damagedSpy.wait()); QCOMPARE(serverSurface->trackedDamage(), QRegion(0, 0, 100, 100)); QCOMPARE(serverSurface->damage(), QRegion(0, 0, 100, 100)); // resetting the tracked damage should empty it serverSurface->resetTrackedDamage(); QVERIFY(serverSurface->trackedDamage().isEmpty()); // but not affect the actual damage QCOMPARE(serverSurface->damage(), QRegion(0, 0, 100, 100)); // let's damage some parts of the surface QPainter p; p.begin(&image); p.fillRect(QRect(0, 0, 10, 10), Qt::blue); p.end(); s->attachBuffer(m_shm->createBuffer(image)); s->damage(QRect(0, 0, 10, 10)); s->commit(Surface::CommitFlag::None); QVERIFY(damagedSpy.wait()); QCOMPARE(serverSurface->trackedDamage(), QRegion(0, 0, 10, 10)); QCOMPARE(serverSurface->damage(), QRegion(0, 0, 10, 10)); // and damage some part completely not bounding to the current damage region p.begin(&image); p.fillRect(QRect(50, 40, 20, 30), Qt::blue); p.end(); s->attachBuffer(m_shm->createBuffer(image)); s->damage(QRect(50, 40, 20, 30)); s->commit(Surface::CommitFlag::None); QVERIFY(damagedSpy.wait()); QCOMPARE(serverSurface->trackedDamage(), QRegion(0, 0, 10, 10).united(QRegion(50, 40, 20, 30))); QCOMPARE(serverSurface->trackedDamage().rectCount(), 2); QCOMPARE(serverSurface->damage(), QRegion(50, 40, 20, 30)); // now let's reset the tracked damage again serverSurface->resetTrackedDamage(); QVERIFY(serverSurface->trackedDamage().isEmpty()); // but not affect the actual damage QCOMPARE(serverSurface->damage(), QRegion(50, 40, 20, 30)); } void TestWaylandSurface::testSurfaceAt() { // this test verifies that surfaceAt(const QPointF&) works as expected for the case of no children using namespace KWayland::Client; using namespace KWayland::Server; // create surface QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer s(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); // a newly created surface should not be mapped and not provide a surface at a position QVERIFY(!serverSurface->isMapped()); QVERIFY(!serverSurface->surfaceAt(QPointF(0, 0))); // let's damage this surface QSignalSpy sizeChangedSpy(serverSurface, &SurfaceInterface::sizeChanged); QVERIFY(sizeChangedSpy.isValid()); QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::red); s->attachBuffer(m_shm->createBuffer(image)); s->damage(QRect(0, 0, 100, 100)); s->commit(Surface::CommitFlag::None); QVERIFY(sizeChangedSpy.wait()); // now the surface is mapped and surfaceAt should give the surface QVERIFY(serverSurface->isMapped()); QCOMPARE(serverSurface->surfaceAt(QPointF(0, 0)), serverSurface); QCOMPARE(serverSurface->surfaceAt(QPointF(100, 100)), serverSurface); // outside the geometry it should not give a surface QVERIFY(!serverSurface->surfaceAt(QPointF(101, 101))); QVERIFY(!serverSurface->surfaceAt(QPointF(-1, -1))); } void TestWaylandSurface::testDestroyAttachedBuffer() { // this test verifies that destroying of a buffer attached to a surface works using namespace KWayland::Client; using namespace KWayland::Server; // create surface QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer s(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); // let's damage this surface QSignalSpy damagedSpy(serverSurface, &SurfaceInterface::damaged); QVERIFY(damagedSpy.isValid()); QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::red); s->attachBuffer(m_shm->createBuffer(image)); s->damage(QRect(0, 0, 100, 100)); s->commit(Surface::CommitFlag::None); QVERIFY(damagedSpy.wait()); QVERIFY(serverSurface->buffer()); // attach another buffer image.fill(Qt::blue); s->attachBuffer(m_shm->createBuffer(image)); m_connection->flush(); // Let's try to destroy it QSignalSpy destroySpy(serverSurface->buffer(), &BufferInterface::aboutToBeDestroyed); QVERIFY(destroySpy.isValid()); delete m_shm; m_shm = nullptr; QVERIFY(destroySpy.wait()); // TODO: should this emit unmapped? QVERIFY(!serverSurface->buffer()); } void TestWaylandSurface::testDestroyWithPendingCallback() { // this test tries to verify that destroying a surface with a pending callback works correctly // first create surface using namespace KWayland::Client; using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); // now render to it QImage img(QSize(10, 10), QImage::Format_ARGB32_Premultiplied); img.fill(Qt::black); auto b = m_shm->createBuffer(img); s->attachBuffer(b); s->damage(QRect(0, 0, 10, 10)); // add some frame callbacks for (int i = 0; i < 1000; i++) { wl_surface_frame(*s); } s->commit(KWayland::Client::Surface::CommitFlag::FrameCallback); QSignalSpy damagedSpy(serverSurface, &SurfaceInterface::damaged); QVERIFY(damagedSpy.isValid()); QVERIFY(damagedSpy.wait()); // now try to destroy the Surface again QSignalSpy destroyedSpy(serverSurface, &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); s.reset(); QVERIFY(destroyedSpy.wait()); } void TestWaylandSurface::testDisconnect() { // this test verifies that the server side correctly tears down the resources when the client disconnects using namespace KWayland::Client; using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); // destroy client QSignalSpy clientDisconnectedSpy(serverSurface->client(), &ClientConnection::disconnected); QVERIFY(clientDisconnectedSpy.isValid()); QSignalSpy surfaceDestroyedSpy(serverSurface, &QObject::destroyed); QVERIFY(surfaceDestroyedSpy.isValid()); if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } QVERIFY(clientDisconnectedSpy.wait()); QCOMPARE(clientDisconnectedSpy.count(), 1); if (surfaceDestroyedSpy.isEmpty()) { QVERIFY(surfaceDestroyedSpy.wait()); } QTRY_COMPARE(surfaceDestroyedSpy.count(), 1); s->destroy(); m_shm->destroy(); m_compositor->destroy(); m_queue->destroy(); + m_idleInhibitManager->destroy(); } void TestWaylandSurface::testOutput() { // This test verifies that the enter/leave are sent correctly to the Client using namespace KWayland::Client; using namespace KWayland::Server; qRegisterMetaType(); QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); QVERIFY(s->outputs().isEmpty()); QSignalSpy enteredSpy(s.data(), &Surface::outputEntered); QVERIFY(enteredSpy.isValid()); QSignalSpy leftSpy(s.data(), &Surface::outputLeft); QVERIFY(leftSpy.isValid()); // wait for the surface on the Server side QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); QCOMPARE(serverSurface->outputs(), QVector()); // create another registry to get notified about added outputs Registry registry; registry.setEventQueue(m_queue); QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced); QVERIFY(allAnnounced.isValid()); registry.create(m_connection); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(allAnnounced.wait()); QSignalSpy outputAnnouncedSpy(®istry, &Registry::outputAnnounced); QVERIFY(outputAnnouncedSpy.isValid()); auto serverOutput = m_display->createOutput(m_display); serverOutput->create(); QVERIFY(outputAnnouncedSpy.wait()); QScopedPointer clientOutput(registry.createOutput(outputAnnouncedSpy.first().first().value(), outputAnnouncedSpy.first().last().value())); QVERIFY(clientOutput->isValid()); m_connection->flush(); m_display->dispatchEvents(); // now enter it serverSurface->setOutputs(QVector{serverOutput}); QCOMPARE(serverSurface->outputs(), QVector{serverOutput}); QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.count(), 1); QCOMPARE(enteredSpy.first().first().value(), clientOutput.data()); QCOMPARE(s->outputs(), QVector{clientOutput.data()}); // adding to same should not trigger serverSurface->setOutputs(QVector{serverOutput}); // leave again serverSurface->setOutputs(QVector()); QCOMPARE(serverSurface->outputs(), QVector()); QVERIFY(leftSpy.wait()); QCOMPARE(enteredSpy.count(), 1); QCOMPARE(leftSpy.count(), 1); QCOMPARE(leftSpy.first().first().value(), clientOutput.data()); QCOMPARE(s->outputs(), QVector()); // leave again should not trigger serverSurface->setOutputs(QVector()); // and enter again, just to verify serverSurface->setOutputs(QVector{serverOutput}); QCOMPARE(serverSurface->outputs(), QVector{serverOutput}); QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.count(), 2); QCOMPARE(leftSpy.count(), 1); //delete output client is on. //client should get an exit and be left on no outputs (which is allowed) serverOutput->deleteLater(); QVERIFY(leftSpy.wait()); QCOMPARE(serverSurface->outputs(), QVector()); } +void TestWaylandSurface::testInhibit() +{ + using namespace KWayland::Client; + using namespace KWayland::Server; + QScopedPointer s(m_compositor->createSurface()); + // wait for the surface on the Server side + QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); + QVERIFY(surfaceCreatedSpy.isValid()); + QVERIFY(surfaceCreatedSpy.wait()); + auto serverSurface = surfaceCreatedSpy.first().first().value(); + QVERIFY(serverSurface); + QCOMPARE(serverSurface->inhibitsIdle(), false); + + QSignalSpy inhibitsChangedSpy(serverSurface, &SurfaceInterface::inhibitsIdleChanged); + QVERIFY(inhibitsChangedSpy.isValid()); + + // now create an idle inhibition + QScopedPointer inhibitor1(m_idleInhibitManager->createInhibitor(s.data())); + QVERIFY(inhibitsChangedSpy.wait()); + QCOMPARE(serverSurface->inhibitsIdle(), true); + + // creating a second idle inhibition should not trigger the signal + QScopedPointer inhibitor2(m_idleInhibitManager->createInhibitor(s.data())); + QVERIFY(!inhibitsChangedSpy.wait()); + QCOMPARE(serverSurface->inhibitsIdle(), true); + + // and also deleting the first inhibitor should not yet change the inhibition + inhibitor1.reset(); + QVERIFY(!inhibitsChangedSpy.wait()); + QCOMPARE(serverSurface->inhibitsIdle(), true); + + // but deleting also the second inhibitor should trigger + inhibitor2.reset(); + QVERIFY(inhibitsChangedSpy.wait()); + QCOMPARE(serverSurface->inhibitsIdle(), false); + QCOMPARE(inhibitsChangedSpy.count(), 2); + + // recreate inhibitor1 should inhibit again + inhibitor1.reset(m_idleInhibitManager->createInhibitor(s.data())); + QVERIFY(inhibitsChangedSpy.wait()); + QCOMPARE(serverSurface->inhibitsIdle(), true); + // and destroying should uninhibit + inhibitor1.reset(); + QVERIFY(inhibitsChangedSpy.wait()); + QCOMPARE(serverSurface->inhibitsIdle(), false); + QCOMPARE(inhibitsChangedSpy.count(), 4); +} + QTEST_GUILESS_MAIN(TestWaylandSurface) #include "test_wayland_surface.moc" diff --git a/autotests/client/test_xdg_foreign.cpp b/autotests/client/test_xdg_foreign.cpp index 4c28614..05032ae 100644 --- a/autotests/client/test_xdg_foreign.cpp +++ b/autotests/client/test_xdg_foreign.cpp @@ -1,401 +1,399 @@ /******************************************************************** Copyright 2014 Martin Gräßlin Copyright 2017 Marco Martin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ // Qt #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/region.h" #include "../../src/client/registry.h" #include "../../src/client/surface.h" #include "../../src/client/xdgforeign.h" #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/surface_interface.h" #include "../../src/server/xdgforeign_interface.h" using namespace KWayland::Client; class TestForeign : public QObject { Q_OBJECT public: explicit TestForeign(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testExport(); void testDeleteImported(); void testDeleteChildSurface(); void testDeleteParentSurface(); void testDeleteExported(); void testExportTwoTimes(); void testImportTwoTimes(); private: void doExport(); KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Server::XdgForeignInterface *m_foreignInterface; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::EventQueue *m_queue; KWayland::Client::XdgExporter *m_exporter; KWayland::Client::XdgImporter *m_importer; QPointer m_exportedSurface; QPointer m_exportedSurfaceInterface; QPointer m_exported; QPointer m_imported; QPointer m_childSurface; QPointer m_childSurfaceInterface; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwayland-test-xdg-foreign-0"); TestForeign::TestForeign(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_queue(nullptr) , m_exporter(nullptr) , m_importer(nullptr) , m_thread(nullptr) { } void TestForeign::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); qRegisterMetaType("KWayland::Server::SurfaceInterface"); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); Registry registry; QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); QVERIFY(compositorSpy.isValid()); QSignalSpy exporterSpy(®istry, &Registry::exporterUnstableV2Announced); QVERIFY(exporterSpy.isValid()); QSignalSpy importerSpy(®istry, &Registry::importerUnstableV2Announced); QVERIFY(importerSpy.isValid()); QVERIFY(!registry.eventQueue()); registry.setEventQueue(m_queue); QCOMPARE(registry.eventQueue(), m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); QVERIFY(compositorSpy.wait()); m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); m_foreignInterface = m_display->createXdgForeignInterface(m_display); m_foreignInterface->create(); QVERIFY(m_foreignInterface->isValid()); QVERIFY(exporterSpy.wait()); //Both importer and exporter should have been triggered by now QCOMPARE(exporterSpy.count(), 1); QCOMPARE(importerSpy.count(), 1); m_exporter = registry.createXdgExporter(exporterSpy.first().first().value(), exporterSpy.first().last().value(), this); m_importer = registry.createXdgImporter(importerSpy.first().first().value(), importerSpy.first().last().value(), this); } void TestForeign::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } - //some tests delete it beforehand - if (m_exportedSurfaceInterface) { - QSignalSpy exportedSurfaceDestroyedSpy(m_exportedSurfaceInterface.data(), &QObject::destroyed); - QVERIFY(exportedSurfaceDestroyedSpy.isValid()); - CLEANUP(m_exportedSurface) - exportedSurfaceDestroyedSpy.wait(); - } + CLEANUP(m_exportedSurfaceInterface) + CLEANUP(m_childSurfaceInterface) - if (m_childSurfaceInterface) { - QSignalSpy childSurfaceDestroyedSpy(m_childSurfaceInterface.data(), &QObject::destroyed); - QVERIFY(childSurfaceDestroyedSpy.isValid()); - CLEANUP(m_childSurface) - childSurfaceDestroyedSpy.wait(); - } CLEANUP(m_compositor) CLEANUP(m_exporter) CLEANUP(m_importer) CLEANUP(m_queue) if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } CLEANUP(m_compositorInterface) CLEANUP(m_foreignInterface) - CLEANUP(m_display) + + //internally there are some deleteLaters on exported interfaces + //we want them processed before we delete the connection + if (m_display) { + QSignalSpy destroyedSpy(m_display, &QObject::destroyed); + m_display->deleteLater(); + m_display = nullptr; + destroyedSpy.wait(); + } + #undef CLEANUP } void TestForeign::doExport() { QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); m_exportedSurface = m_compositor->createSurface(); QVERIFY(serverSurfaceCreated.wait()); m_exportedSurfaceInterface = serverSurfaceCreated.first().first().value(); //Export a window - m_exported = m_exporter->exportTopLevel(m_exportedSurface, this); + m_exported = m_exporter->exportTopLevel(m_exportedSurface, m_connection); QVERIFY(m_exported->handle().isEmpty()); QSignalSpy doneSpy(m_exported.data(), &XdgExported::done); QVERIFY(doneSpy.wait()); QVERIFY(!m_exported->handle().isEmpty()); QSignalSpy transientSpy(m_foreignInterface, &KWayland::Server::XdgForeignInterface::transientChanged); QVERIFY(transientSpy.isValid()); //Import the just exported window - m_imported = m_importer->importTopLevel(m_exported->handle(), this); + m_imported = m_importer->importTopLevel(m_exported->handle(), m_connection); QVERIFY(m_imported->isValid()); QSignalSpy childSurfaceInterfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); m_childSurface = m_compositor->createSurface(); QVERIFY(childSurfaceInterfaceCreated.wait()); m_childSurfaceInterface = childSurfaceInterfaceCreated.first().first().value(); m_childSurface->commit(Surface::CommitFlag::None); m_imported->setParentOf(m_childSurface); QVERIFY(transientSpy.wait()); QCOMPARE(transientSpy.first().first().value(), m_childSurfaceInterface.data()); QCOMPARE(transientSpy.first().at(1).value(), m_exportedSurfaceInterface.data()); //transientFor api QCOMPARE(m_foreignInterface->transientFor(m_childSurfaceInterface), m_exportedSurfaceInterface.data()); } void TestForeign::testExport() { doExport(); } void TestForeign::testDeleteImported() { doExport(); QSignalSpy transientSpy(m_foreignInterface, &KWayland::Server::XdgForeignInterface::transientChanged); QVERIFY(transientSpy.isValid()); m_imported->deleteLater(); m_imported = nullptr; QVERIFY(transientSpy.wait()); QCOMPARE(transientSpy.first().first().value(), m_childSurfaceInterface.data()); QVERIFY(!transientSpy.first().at(1).value()); QVERIFY(!m_foreignInterface->transientFor(m_childSurfaceInterface)); } void TestForeign::testDeleteChildSurface() { doExport(); QSignalSpy transientSpy(m_foreignInterface, &KWayland::Server::XdgForeignInterface::transientChanged); QVERIFY(transientSpy.isValid()); m_childSurface->deleteLater(); QVERIFY(transientSpy.wait()); //when the client surface dies, the server one will eventually die too QSignalSpy surfaceDestroyedSpy(m_childSurfaceInterface, SIGNAL(destroyed())); QVERIFY(surfaceDestroyedSpy.wait()); QVERIFY(!transientSpy.first().at(0).value()); QCOMPARE(transientSpy.first().at(1).value(), m_exportedSurfaceInterface.data()); } void TestForeign::testDeleteParentSurface() { doExport(); QSignalSpy transientSpy(m_foreignInterface, &KWayland::Server::XdgForeignInterface::transientChanged); QVERIFY(transientSpy.isValid()); m_exportedSurface->deleteLater(); QSignalSpy exportedSurfaceDestroyedSpy(m_exportedSurfaceInterface.data(), &QObject::destroyed); QVERIFY(exportedSurfaceDestroyedSpy.isValid()); exportedSurfaceDestroyedSpy.wait(); QVERIFY(transientSpy.wait()); QCOMPARE(transientSpy.first().first().value(), m_childSurfaceInterface.data()); QVERIFY(!transientSpy.first().at(1).value()); QVERIFY(!m_foreignInterface->transientFor(m_childSurfaceInterface)); } void TestForeign::testDeleteExported() { doExport(); QSignalSpy transientSpy(m_foreignInterface, &KWayland::Server::XdgForeignInterface::transientChanged); QSignalSpy destroyedSpy(m_imported.data(), &KWayland::Client::XdgImported::importedDestroyed); QVERIFY(transientSpy.isValid()); m_exported->deleteLater(); m_exported = nullptr; QVERIFY(transientSpy.wait()); QVERIFY(destroyedSpy.wait()); QCOMPARE(transientSpy.first().first().value(), m_childSurfaceInterface.data()); QVERIFY(!transientSpy.first().at(1).value()); QVERIFY(!m_foreignInterface->transientFor(m_childSurfaceInterface)); QVERIFY(!m_imported->isValid()); } void TestForeign::testExportTwoTimes() { doExport(); //Export second window - KWayland::Client::XdgExported *exported2 = m_exporter->exportTopLevel(m_exportedSurface, this); + KWayland::Client::XdgExported *exported2 = m_exporter->exportTopLevel(m_exportedSurface, m_connection); QVERIFY(exported2->handle().isEmpty()); QSignalSpy doneSpy(exported2, &XdgExported::done); QVERIFY(doneSpy.wait()); QVERIFY(!exported2->handle().isEmpty()); QSignalSpy transientSpy(m_foreignInterface, &KWayland::Server::XdgForeignInterface::transientChanged); QVERIFY(transientSpy.isValid()); //Import the just exported window - KWayland::Client::XdgImported *imported2 = m_importer->importTopLevel(exported2->handle(), this); + KWayland::Client::XdgImported *imported2 = m_importer->importTopLevel(exported2->handle(), m_connection); QVERIFY(imported2->isValid()); //create a second child surface QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); KWayland::Client::Surface *childSurface2 = m_compositor->createSurface(); QVERIFY(serverSurfaceCreated.wait()); KWayland::Server::SurfaceInterface *childSurface2Interface = serverSurfaceCreated.first().first().value(); imported2->setParentOf(childSurface2); QVERIFY(transientSpy.wait()); QCOMPARE(transientSpy.first().first().value(), childSurface2Interface); QCOMPARE(transientSpy.first().at(1).value(), m_exportedSurfaceInterface.data()); //transientFor api //check the old relationship is still here QCOMPARE(m_foreignInterface->transientFor(m_childSurfaceInterface), m_exportedSurfaceInterface.data()); //check the new relationship QCOMPARE(m_foreignInterface->transientFor(childSurface2Interface), m_exportedSurfaceInterface.data()); } void TestForeign::testImportTwoTimes() { doExport(); QSignalSpy transientSpy(m_foreignInterface, &KWayland::Server::XdgForeignInterface::transientChanged); QVERIFY(transientSpy.isValid()); //Import another time the exported window - KWayland::Client::XdgImported *imported2 = m_importer->importTopLevel(m_exported->handle(), this); + KWayland::Client::XdgImported *imported2 = m_importer->importTopLevel(m_exported->handle(), m_connection); QVERIFY(imported2->isValid()); //create a second child surface QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); KWayland::Client::Surface *childSurface2 = m_compositor->createSurface(); QVERIFY(serverSurfaceCreated.wait()); KWayland::Server::SurfaceInterface *childSurface2Interface = serverSurfaceCreated.first().first().value(); imported2->setParentOf(childSurface2); QVERIFY(transientSpy.wait()); QCOMPARE(transientSpy.first().first().value(), childSurface2Interface); QCOMPARE(transientSpy.first().at(1).value(), m_exportedSurfaceInterface.data()); //transientFor api //check the old relationship is still here QCOMPARE(m_foreignInterface->transientFor(m_childSurfaceInterface), m_exportedSurfaceInterface.data()); //check the new relationship QCOMPARE(m_foreignInterface->transientFor(childSurface2Interface), m_exportedSurfaceInterface.data()); } QTEST_GUILESS_MAIN(TestForeign) #include "test_xdg_foreign.moc" diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index d46326d..befbbe0 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -1,240 +1,286 @@ remove_definitions(-DQT_NO_CAST_FROM_BYTEARRAY) remove_definitions(-DQT_NO_CAST_FROM_ASCII) remove_definitions(-DQT_NO_CAST_TO_ASCII) # needed to access QPA include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS}) set(CLIENT_LIB_SRCS + appmenu.cpp buffer.cpp blur.cpp compositor.cpp connection_thread.cpp contrast.cpp slide.cpp event_queue.cpp datadevice.cpp datadevicemanager.cpp dataoffer.cpp datasource.cpp dpms.cpp fakeinput.cpp fullscreen_shell.cpp idle.cpp + idleinhibit.cpp keyboard.cpp remote_access.cpp outputconfiguration.cpp outputmanagement.cpp outputdevice.cpp logging.cpp output.cpp pointer.cpp pointerconstraints.cpp pointergestures.cpp plasmashell.cpp plasmawindowmanagement.cpp plasmawindowmodel.cpp region.cpp registry.cpp relativepointer.cpp seat.cpp server_decoration.cpp + server_decoration_palette.cpp shadow.cpp shell.cpp shm_pool.cpp subcompositor.cpp subsurface.cpp surface.cpp touch.cpp textinput.cpp textinput_v0.cpp textinput_v2.cpp xdgshell.cpp xdgshell_v5.cpp xdgforeign_v2.cpp xdgforeign.cpp xdgshell_v6.cpp ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/fullscreen-shell.xml BASENAME fullscreen-shell ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/output-management.xml BASENAME output-management ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/outputdevice.xml BASENAME org_kde_kwin_outputdevice ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/plasma-shell.xml BASENAME plasma-shell ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/plasma-window-management.xml BASENAME plasma-window-management ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/idle.xml BASENAME idle ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/fake-input.xml BASENAME fake-input ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/shadow.xml BASENAME shadow ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/blur.xml BASENAME blur ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/contrast.xml BASENAME contrast ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/slide.xml BASENAME slide ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/dpms.xml BASENAME dpms ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/server-decoration.xml BASENAME server-decoration ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/text-input.xml BASENAME text-input-v0 ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/text-input-unstable-v2.xml BASENAME text-input-v2 ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/xdg-shell-unstable-v5.xml BASENAME xdg-shell-v5 ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/xdg-shell-unstable-v6.xml BASENAME xdg-shell-v6 ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/relative-pointer-unstable-v1.xml BASENAME relativepointer-unstable-v1 ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/pointer-gestures-unstable-v1.xml BASENAME pointer-gestures-unstable-v1 ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/pointer-constraints-unstable-v1.xml BASENAME pointer-constraints-unstable-v1 ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/xdg-foreign-unstable-v2.xml BASENAME xdg-foreign-unstable-v2 ) +ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS + PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/idle-inhibit-unstable-v1.xml + BASENAME idle-inhibit-unstable-v1 +) +ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS + PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/appmenu.xml + BASENAME appmenu +) +ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS + PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/server-decoration-palette.xml + BASENAME server-decoration-palette +) + +set(CLIENT_GENERATED_FILES + ${CMAKE_CURRENT_BINARY_DIR}/wayland-fullscreen-shell-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-output-management-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-org_kde_kwin_outputdevice-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-shell-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-window-management-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-idle-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-fake-input-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-shadow-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-blur-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-contrast-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-slide-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-dpms-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-server-decoration-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-server-decoration-palette-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-text-input-v0-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-text-input-v2-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-xdg-shell-v5-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-xdg-shell-v6-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-relativepointer-unstable-v1-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-pointer-gestures-unstable-v1-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-pointer-constraints-unstable-v1-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-xdg-foreign-unstable-v2-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-idle-inhibit-unstable-v1-client-protocol.h +) + +set_source_files_properties(${CLIENT_GENERATED_FILES} PROPERTIES SKIP_AUTOMOC ON) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/remote-access.xml BASENAME remote-access ) add_library(KF5WaylandClient ${CLIENT_LIB_SRCS}) generate_export_header(KF5WaylandClient BASE_NAME KWaylandClient EXPORT_FILE_NAME KWayland/Client/kwaylandclient_export.h ) add_library(KF5::WaylandClient ALIAS KF5WaylandClient) target_include_directories(KF5WaylandClient INTERFACE "$") target_link_libraries(KF5WaylandClient PUBLIC Qt5::Gui PRIVATE Wayland::Client Qt5::Concurrent ) set_target_properties(KF5WaylandClient PROPERTIES VERSION ${KWAYLAND_VERSION_STRING} SOVERSION ${KWAYLAND_SOVERSION} EXPORT_NAME WaylandClient ) install(TARGETS KF5WaylandClient EXPORT KF5WaylandTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) set(CLIENT_LIB_HEADERS ${CMAKE_CURRENT_BINARY_DIR}/KWayland/Client/kwaylandclient_export.h + appmenu.h blur.h buffer.h compositor.h connection_thread.h contrast.h event_queue.h datadevice.h datadevicemanager.h dataoffer.h datasource.h dpms.h fakeinput.h fullscreen_shell.h idle.h + idleinhibit.h keyboard.h remote_access.h outputconfiguration.h outputmanagement.h outputdevice.h output.h pointer.h pointerconstraints.h plasmashell.h plasmawindowmanagement.h plasmawindowmodel.h pointergestures.h region.h registry.h relativepointer.h seat.h server_decoration.h + server_decoration_palette.h shadow.h shell.h shm_pool.h slide.h subcompositor.h subsurface.h surface.h touch.h textinput.h xdgshell.h xdgforeign_v2.h ) install(FILES ${CLIENT_LIB_HEADERS} DESTINATION ${KF5_INCLUDE_INSTALL_DIR}/KWayland/Client COMPONENT Devel ) # make available to ecm_add_qch in parent folder set(KWaylandClient_APIDOX_SRCS ${CLIENT_LIB_HEADERS} PARENT_SCOPE) include(ECMGeneratePriFile) ecm_generate_pri_file(BASE_NAME KWaylandClient LIB_NAME KF5WaylandClient DEPS "core" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) diff --git a/src/client/appmenu.cpp b/src/client/appmenu.cpp new file mode 100644 index 0000000..cb38382 --- /dev/null +++ b/src/client/appmenu.cpp @@ -0,0 +1,184 @@ +/**************************************************************************** +Copyright 2017 David Edmundson + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#include "appmenu.h" +#include "event_queue.h" +#include "surface.h" +#include "wayland_pointer_p.h" + +#include + +namespace KWayland +{ +namespace Client +{ + +class AppMenuManager::Private +{ +public: + Private() = default; + + void setup(org_kde_kwin_appmenu_manager *arg); + + WaylandPointer appmenumanager; + EventQueue *queue = nullptr; +}; + +AppMenuManager::AppMenuManager(QObject *parent) + : QObject(parent) + , d(new Private) +{ +} + +void AppMenuManager::Private::setup(org_kde_kwin_appmenu_manager *arg) +{ + Q_ASSERT(arg); + Q_ASSERT(!appmenumanager); + appmenumanager.setup(arg); +} + +AppMenuManager::~AppMenuManager() +{ + release(); +} + +void AppMenuManager::setup(org_kde_kwin_appmenu_manager *appmenumanager) +{ + d->setup(appmenumanager); +} + +void AppMenuManager::release() +{ + d->appmenumanager.release(); +} + +void AppMenuManager::destroy() +{ + d->appmenumanager.destroy(); +} + +AppMenuManager::operator org_kde_kwin_appmenu_manager*() { + return d->appmenumanager; +} + +AppMenuManager::operator org_kde_kwin_appmenu_manager*() const { + return d->appmenumanager; +} + +bool AppMenuManager::isValid() const +{ + return d->appmenumanager.isValid(); +} + +void AppMenuManager::setEventQueue(EventQueue *queue) +{ + d->queue = queue; +} + +EventQueue *AppMenuManager::eventQueue() +{ + return d->queue; +} + +AppMenu *AppMenuManager::create(Surface *surface, QObject *parent) +{ + Q_ASSERT(isValid()); + auto p = new AppMenu(parent); + auto w = org_kde_kwin_appmenu_manager_create(d->appmenumanager, *surface); + if (d->queue) { + d->queue->addProxy(w); + } + p->setup(w); + return p; +} + +class AppMenu::Private +{ +public: + Private(AppMenu *q); + + void setup(org_kde_kwin_appmenu *arg); + + WaylandPointer appmenu; + +private: + AppMenu *q; +}; + +AppMenu::Private::Private(AppMenu *q) + : q(q) +{ +} + +AppMenu::AppMenu(QObject *parent) + : QObject(parent) + , d(new Private(this)) +{ +} + +void AppMenu::Private::setup(org_kde_kwin_appmenu *arg) +{ + Q_ASSERT(arg); + Q_ASSERT(!appmenu); + appmenu.setup(arg); +} + +AppMenu::~AppMenu() +{ + release(); +} + +void AppMenu::setup(org_kde_kwin_appmenu *appmenu) +{ + d->setup(appmenu); +} + +void AppMenu::release() +{ + d->appmenu.release(); +} + +void AppMenu::destroy() +{ + d->appmenu.destroy(); +} + +AppMenu::operator org_kde_kwin_appmenu*() { + return d->appmenu; +} + +AppMenu::operator org_kde_kwin_appmenu*() const { + return d->appmenu; +} + +bool AppMenu::isValid() const +{ + return d->appmenu.isValid(); +} + +void AppMenu::setAddress(const QString &serviceName, const QString &objectPath) +{ + Q_ASSERT(isValid()); + org_kde_kwin_appmenu_set_address(d->appmenu, serviceName.toLatin1(), objectPath.toLatin1()); +} + + +} +} + diff --git a/src/client/appmenu.h b/src/client/appmenu.h new file mode 100644 index 0000000..fe78325 --- /dev/null +++ b/src/client/appmenu.h @@ -0,0 +1,202 @@ +/**************************************************************************** +Copyright 2017 David Edmundson + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#ifndef KWAYLAND_CLIENT_APPMENU_H +#define KWAYLAND_CLIENT_APPMENU_H + +#include + +#include + +struct org_kde_kwin_appmenu_manager; +struct org_kde_kwin_appmenu; + +namespace KWayland +{ +namespace Client +{ + +class EventQueue; +class Surface; +class AppMenu; + +/** + * @short Wrapper for the org_kde_kwin_appmenu_manager interface. + * + * This class provides a convenient wrapper for the org_kde_kwin_appmenu_manager interface. + * + * To use this class one needs to interact with the Registry. There are two + * possible ways to create the AppMenuManager interface: + * @code + * AppMenuManager *c = registry->createAppMenuManager(name, version); + * @endcode + * + * This creates the AppMenuManager and sets it up directly. As an alternative this + * can also be done in a more low level way: + * @code + * AppMenuManager *c = new AppMenuManager; + * c->setup(registry->bindAppMenuManager(name, version)); + * @endcode + * + * The AppMenuManager can be used as a drop-in replacement for any org_kde_kwin_appmenu_manager + * pointer as it provides matching cast operators. + * + * @see Registry + * @since 5.42 + **/ +class KWAYLANDCLIENT_EXPORT AppMenuManager : public QObject +{ + Q_OBJECT +public: + /** + * Creates a new AppMenuManager. + * Note: after constructing the AppMenuManager it is not yet valid and one needs + * to call setup. In order to get a ready to use AppMenuManager prefer using + * Registry::createAppMenuManager. + **/ + explicit AppMenuManager(QObject *parent = nullptr); + virtual ~AppMenuManager(); + + /** + * Setup this AppMenuManager to manage the @p appmenumanager. + * When using Registry::createAppMenuManager there is no need to call this + * method. + **/ + void setup(org_kde_kwin_appmenu_manager *appmenumanager); + /** + * @returns @c true if managing a org_kde_kwin_appmenu_manager. + **/ + bool isValid() const; + /** + * Releases the org_kde_kwin_appmenu_manager interface. + * After the interface has been released the AppMenuManager instance is no + * longer valid and can be setup with another org_kde_kwin_appmenu_manager interface. + **/ + void release(); + /** + * Destroys the data held by this AppMenuManager. + * This method is supposed to be used when the connection to the Wayland + * server goes away. If the connection is not valid anymore, it's not + * possible to call release anymore as that calls into the Wayland + * connection and the call would fail. This method cleans up the data, so + * that the instance can be deleted or set up to a new org_kde_kwin_appmenu_manager interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, appmenumanager, &AppMenuManager::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + /** + * Sets the @p queue to use for creating objects with this AppMenuManager. + **/ + void setEventQueue(EventQueue *queue); + /** + * @returns The event queue to use for creating objects with this AppMenuManager. + **/ + EventQueue *eventQueue(); + + AppMenu *create(Surface *surface, QObject *parent = nullptr); + + operator org_kde_kwin_appmenu_manager*(); + operator org_kde_kwin_appmenu_manager*() const; + +Q_SIGNALS: + /** + * The corresponding global for this interface on the Registry got removed. + * + * This signal gets only emitted if the AppMenuManager got created by + * Registry::createAppMenuManager + **/ + void removed(); + +private: + class Private; + QScopedPointer d; +}; + +/** + * + * @since 5.42 + **/ +class KWAYLANDCLIENT_EXPORT AppMenu : public QObject +{ + Q_OBJECT +public: + virtual ~AppMenu(); + + /** + * Setup this Appmenu to manage the @p appmenu. + * When using AppMenuManager::createAppmenu there is no need to call this + * method. + **/ + void setup(org_kde_kwin_appmenu *appmenu); + /** + * @returns @c true if managing a org_kde_kwin_appmenu. + **/ + bool isValid() const; + /** + * Releases the org_kde_kwin_appmenu interface. + * After the interface has been released the Appmenu instance is no + * longer valid and can be setup with another org_kde_kwin_appmenu interface. + **/ + void release(); + /** + * Destroys the data held by this Appmenu. + * This method is supposed to be used when the connection to the Wayland + * server goes away. If the connection is not valid anymore, it's not + * possible to call release anymore as that calls into the Wayland + * connection and the call would fail. This method cleans up the data, so + * that the instance can be deleted or set up to a new org_kde_kwin_appmenu interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, appmenu, &Appmenu::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + /** + * Sets the appmenu address. The DBus object should be registered before making this call + * Strings should be valid DBus formatted names, in latin1. + */ + void setAddress(const QString & serviceName, const QString & objectPath); + + operator org_kde_kwin_appmenu*(); + operator org_kde_kwin_appmenu*() const; + +private: + friend class AppMenuManager; + explicit AppMenu(QObject *parent = nullptr); + class Private; + QScopedPointer d; +}; + + +} +} + +#endif diff --git a/src/client/datadevice.cpp b/src/client/datadevice.cpp index f66413a..34bc625 100644 --- a/src/client/datadevice.cpp +++ b/src/client/datadevice.cpp @@ -1,260 +1,263 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "datadevice.h" #include "dataoffer.h" #include "datasource.h" #include "surface.h" #include "wayland_pointer_p.h" // Qt #include // Wayland #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN DataDevice::Private { public: explicit Private(DataDevice *q); void setup(wl_data_device *d); WaylandPointer device; QScopedPointer selectionOffer; struct Drag { - DataOffer *offer = nullptr; + QPointer offer; QPointer surface; }; Drag drag; private: void dataOffer(wl_data_offer *id); void selection(wl_data_offer *id); void dragEnter(quint32 serial, const QPointer &surface, const QPointF &relativeToSurface, wl_data_offer *dataOffer); void dragLeft(); static void dataOfferCallback(void *data, wl_data_device *dataDevice, wl_data_offer *id); static void enterCallback(void *data, wl_data_device *dataDevice, uint32_t serial, wl_surface *surface, wl_fixed_t x, wl_fixed_t y, wl_data_offer *id); static void leaveCallback(void *data, wl_data_device *dataDevice); static void motionCallback(void *data, wl_data_device *dataDevice, uint32_t time, wl_fixed_t x, wl_fixed_t y); static void dropCallback(void *data, wl_data_device *dataDevice); static void selectionCallback(void *data, wl_data_device *dataDevice, wl_data_offer *id); static const struct wl_data_device_listener s_listener; DataDevice *q; DataOffer *lastOffer = nullptr; }; const wl_data_device_listener DataDevice::Private::s_listener = { dataOfferCallback, enterCallback, leaveCallback, motionCallback, dropCallback, selectionCallback }; void DataDevice::Private::dataOfferCallback(void *data, wl_data_device *dataDevice, wl_data_offer *id) { auto d = reinterpret_cast(data); Q_ASSERT(d->device == dataDevice); d->dataOffer(id); } void DataDevice::Private::dataOffer(wl_data_offer *id) { Q_ASSERT(!lastOffer); lastOffer = new DataOffer(q, id); Q_ASSERT(lastOffer->isValid()); } void DataDevice::Private::enterCallback(void *data, wl_data_device *dataDevice, uint32_t serial, wl_surface *surface, wl_fixed_t x, wl_fixed_t y, wl_data_offer *id) { auto d = reinterpret_cast(data); Q_ASSERT(d->device == dataDevice); d->dragEnter(serial, QPointer(Surface::get(surface)), QPointF(wl_fixed_to_double(x), wl_fixed_to_double(y)), id); } void DataDevice::Private::dragEnter(quint32 serial, const QPointer &surface, const QPointF &relativeToSurface, wl_data_offer *dataOffer) { drag.surface = surface; Q_ASSERT(*lastOffer == dataOffer); drag.offer = lastOffer; lastOffer = nullptr; emit q->dragEntered(serial, relativeToSurface); } void DataDevice::Private::leaveCallback(void *data, wl_data_device *dataDevice) { auto d = reinterpret_cast(data); Q_ASSERT(d->device == dataDevice); d->dragLeft(); } void DataDevice::Private::dragLeft() { if (drag.offer) { delete drag.offer; } drag = Drag(); emit q->dragLeft(); } void DataDevice::Private::motionCallback(void *data, wl_data_device *dataDevice, uint32_t time, wl_fixed_t x, wl_fixed_t y) { auto d = reinterpret_cast(data); Q_ASSERT(d->device == dataDevice); emit d->q->dragMotion(QPointF(wl_fixed_to_double(x), wl_fixed_to_double(y)), time); } void DataDevice::Private::dropCallback(void *data, wl_data_device *dataDevice) { auto d = reinterpret_cast(data); Q_ASSERT(d->device == dataDevice); emit d->q->dropped(); } void DataDevice::Private::selectionCallback(void *data, wl_data_device *dataDevice, wl_data_offer *id) { auto d = reinterpret_cast(data); Q_ASSERT(d->device == dataDevice); d->selection(id); } void DataDevice::Private::selection(wl_data_offer *id) { if (!id) { selectionOffer.reset(); emit q->selectionCleared(); return; } Q_ASSERT(*lastOffer == id); selectionOffer.reset(lastOffer); lastOffer = nullptr; emit q->selectionOffered(selectionOffer.data()); } DataDevice::Private::Private(DataDevice *q) : q(q) { } void DataDevice::Private::setup(wl_data_device *d) { Q_ASSERT(d); Q_ASSERT(!device.isValid()); device.setup(d); wl_data_device_add_listener(device, &s_listener, this); } DataDevice::DataDevice(QObject *parent) : QObject(parent) , d(new Private(this)) { } DataDevice::~DataDevice() { + if (d->drag.offer) { + delete d->drag.offer; + } release(); } void DataDevice::destroy() { d->device.destroy(); } void DataDevice::release() { d->device.release(); } bool DataDevice::isValid() const { return d->device.isValid(); } void DataDevice::setup(wl_data_device *dataDevice) { d->setup(dataDevice); } void DataDevice::startDragInternally(quint32 serial, Surface *origin, Surface *icon) { startDrag(serial, nullptr, origin, icon); } namespace { static wl_data_source *dataSource(const DataSource *source) { if (!source) { return nullptr; } return *source; } } void DataDevice::startDrag(quint32 serial, DataSource *source, Surface *origin, Surface *icon) { wl_data_device_start_drag(d->device, dataSource(source), *origin, icon ? (wl_surface*)*icon : nullptr, serial); } void DataDevice::setSelection(quint32 serial, DataSource *source) { wl_data_device_set_selection(d->device, dataSource(source), serial); } void DataDevice::clearSelection(quint32 serial) { setSelection(serial); } DataOffer *DataDevice::offeredSelection() const { return d->selectionOffer.data(); } QPointer DataDevice::dragSurface() const { return d->drag.surface; } DataOffer *DataDevice::dragOffer() const { return d->drag.offer; } DataDevice::operator wl_data_device*() { return d->device; } DataDevice::operator wl_data_device*() const { return d->device; } } } diff --git a/src/client/datadevicemanager.h b/src/client/datadevicemanager.h index d899840..1834bc2 100644 --- a/src/client/datadevicemanager.h +++ b/src/client/datadevicemanager.h @@ -1,142 +1,156 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #ifndef WAYLAND_DATA_DEVICE_MANAGER_H #define WAYLAND_DATA_DEVICE_MANAGER_H #include #include struct wl_data_device_manager; namespace KWayland { namespace Client { class EventQueue; class DataDevice; class DataSource; class Seat; /** * @short Wrapper for the wl_data_device_manager interface. * * This class provides a convenient wrapper for the wl_data_device_manager interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the DataDeviceManager interface: * @code * DataDeviceManager *m = registry->createDataDeviceManager(name, version); * @endcode * * This creates the DataDeviceManager and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * DataDeviceManager *m = new DataDeviceManager; * m->setup(registry->bindDataDeviceManager(name, version)); * @endcode * * The DataDeviceManager can be used as a drop-in replacement for any wl_data_device_manager * pointer as it provides matching cast operators. * * @see Registry **/ class KWAYLANDCLIENT_EXPORT DataDeviceManager : public QObject { Q_OBJECT public: + /** + * Drag and Drop actions supported by DataSource and DataOffer. + * @since 5.42 + **/ + enum class DnDAction { + None = 0, + Copy = 1 << 0, + Move = 1 << 1, + Ask = 1 << 2 + }; + Q_DECLARE_FLAGS(DnDActions, DnDAction) + /** * Creates a new Compositor. * Note: after constructing the Compositor it is not yet valid and one needs * to call setup. In order to get a ready to use Compositor prefer using * Registry::createCompositor. **/ explicit DataDeviceManager(QObject *parent = nullptr); virtual ~DataDeviceManager(); /** * @returns @c true if managing a wl_data_device_manager. **/ bool isValid() const; /** * Setup this DataDeviceManager to manage the @p manager. * When using Registry::createDataDeviceManager there is no need to call this * method. **/ void setup(wl_data_device_manager *manager); /** * Releases the wl_data_device_manager interface. * After the interface has been released the DataDeviceManager instance is no * longer valid and can be setup with another wl_data_device_manager interface. **/ void release(); /** * Destroys the data held by this DataDeviceManager. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new wl_data_device_manager interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * DataDeviceManager gets destroyed. * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating a DataSource. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a DataSource. **/ EventQueue *eventQueue(); DataSource *createDataSource(QObject *parent = nullptr); DataDevice *getDataDevice(Seat *seat, QObject *parent = nullptr); operator wl_data_device_manager*(); operator wl_data_device_manager*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the Compositor got created by * Registry::createDataDeviceManager * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; } } +Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Client::DataDeviceManager::DnDActions) + #endif diff --git a/src/client/dataoffer.cpp b/src/client/dataoffer.cpp index e2db73b..55777b7 100644 --- a/src/client/dataoffer.cpp +++ b/src/client/dataoffer.cpp @@ -1,133 +1,242 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "dataoffer.h" #include "datadevice.h" #include "wayland_pointer_p.h" // Qt #include #include // Wayland #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN DataOffer::Private { public: Private(wl_data_offer *offer, DataOffer *q); WaylandPointer dataOffer; QList mimeTypes; + DataDeviceManager::DnDActions sourceActions = DataDeviceManager::DnDAction::None; + DataDeviceManager::DnDAction selectedAction = DataDeviceManager::DnDAction::None; private: void offer(const QString &mimeType); + void setAction(DataDeviceManager::DnDAction action); static void offerCallback(void *data, wl_data_offer *dataOffer, const char *mimeType); + static void sourceActionsCallback(void *data, wl_data_offer *wl_data_offer, uint32_t source_actions); + static void actionCallback(void *data, wl_data_offer *wl_data_offer, uint32_t dnd_action); DataOffer *q; static const struct wl_data_offer_listener s_listener; }; #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct wl_data_offer_listener DataOffer::Private::s_listener = { - offerCallback + offerCallback, + sourceActionsCallback, + actionCallback }; #endif DataOffer::Private::Private(wl_data_offer *offer, DataOffer *q) : q(q) { dataOffer.setup(offer); wl_data_offer_add_listener(offer, &s_listener, this); } void DataOffer::Private::offerCallback(void *data, wl_data_offer *dataOffer, const char *mimeType) { auto d = reinterpret_cast(data); Q_ASSERT(d->dataOffer == dataOffer); d->offer(QString::fromUtf8(mimeType)); } void DataOffer::Private::offer(const QString &mimeType) { QMimeDatabase db; const auto &m = db.mimeTypeForName(mimeType); if (m.isValid()) { mimeTypes << m; emit q->mimeTypeOffered(m.name()); } } +void DataOffer::Private::sourceActionsCallback(void *data, wl_data_offer *wl_data_offer, uint32_t source_actions) +{ + Q_UNUSED(wl_data_offer) + DataDeviceManager::DnDActions actions; + if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) { + actions |= DataDeviceManager::DnDAction::Copy; + } + if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) { + actions |= DataDeviceManager::DnDAction::Move; + } + if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) { + actions |= DataDeviceManager::DnDAction::Ask; + } + auto d = reinterpret_cast(data); + if (d->sourceActions != actions) { + d->sourceActions = actions; + emit d->q->sourceDragAndDropActionsChanged(); + } +} + +void DataOffer::Private::actionCallback(void *data, wl_data_offer *wl_data_offer, uint32_t dnd_action) +{ + Q_UNUSED(wl_data_offer) + auto d = reinterpret_cast(data); + switch(dnd_action) { + case WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY: + d->setAction(DataDeviceManager::DnDAction::Copy); + break; + case WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE: + d->setAction(DataDeviceManager::DnDAction::Move); + break; + case WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK: + d->setAction(DataDeviceManager::DnDAction::Ask); + break; + case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE: + d->setAction(DataDeviceManager::DnDAction::None); + break; + default: + Q_UNREACHABLE(); + } +} + +void DataOffer::Private::setAction(DataDeviceManager::DnDAction action) +{ + if (action == selectedAction) { + return; + } + selectedAction = action; + emit q->selectedDragAndDropActionChanged(); +} + DataOffer::DataOffer(DataDevice *parent, wl_data_offer *dataOffer) : QObject(parent) , d(new Private(dataOffer, this)) { } DataOffer::~DataOffer() { release(); } void DataOffer::release() { d->dataOffer.release(); } void DataOffer::destroy() { d->dataOffer.destroy(); } bool DataOffer::isValid() const { return d->dataOffer.isValid(); } QList< QMimeType > DataOffer::offeredMimeTypes() const { return d->mimeTypes; } void DataOffer::receive(const QMimeType &mimeType, qint32 fd) { receive(mimeType.name(), fd); } void DataOffer::receive(const QString &mimeType, qint32 fd) { Q_ASSERT(isValid()); wl_data_offer_receive(d->dataOffer, mimeType.toUtf8().constData(), fd); } DataOffer::operator wl_data_offer*() { return d->dataOffer; } DataOffer::operator wl_data_offer*() const { return d->dataOffer; } +void DataOffer::dragAndDropFinished() +{ + Q_ASSERT(isValid()); + if (wl_proxy_get_version(d->dataOffer) < WL_DATA_OFFER_FINISH_SINCE_VERSION) { + return; + } + wl_data_offer_finish(d->dataOffer); +} + +DataDeviceManager::DnDActions DataOffer::sourceDragAndDropActions() const +{ + return d->sourceActions; +} + +void DataOffer::setDragAndDropActions(DataDeviceManager::DnDActions supported, DataDeviceManager::DnDAction preferred) +{ + if (wl_proxy_get_version(d->dataOffer) < WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) { + return; + } + auto toWayland = [] (DataDeviceManager::DnDAction action) { + switch (action) { + case DataDeviceManager::DnDAction::Copy: + return WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + case DataDeviceManager::DnDAction::Move: + return WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + case DataDeviceManager::DnDAction::Ask: + return WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; + case DataDeviceManager::DnDAction::None: + return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + default: + Q_UNREACHABLE(); + } + }; + uint32_t wlSupported = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + if (supported.testFlag(DataDeviceManager::DnDAction::Copy)) { + wlSupported |= toWayland(DataDeviceManager::DnDAction::Copy); + } + if (supported.testFlag(DataDeviceManager::DnDAction::Move)) { + wlSupported |= toWayland(DataDeviceManager::DnDAction::Move); + } + if (supported.testFlag(DataDeviceManager::DnDAction::Ask)) { + wlSupported |= toWayland(DataDeviceManager::DnDAction::Ask); + } + wl_data_offer_set_actions(d->dataOffer, wlSupported, toWayland(preferred)); +} + +DataDeviceManager::DnDAction DataOffer::selectedDragAndDropAction() const +{ + return d->selectedAction; +} + } } diff --git a/src/client/dataoffer.h b/src/client/dataoffer.h index edcebd3..cf954d9 100644 --- a/src/client/dataoffer.h +++ b/src/client/dataoffer.h @@ -1,100 +1,145 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #ifndef WAYLAND_DATAOFFER_H #define WAYLAND_DATAOFFER_H #include #include +#include "datadevicemanager.h" + struct wl_data_offer; class QMimeType; namespace KWayland { namespace Client { class DataDevice; /** * @short Wrapper for the wl_data_offer interface. * * This class is a convenient wrapper for the wl_data_offer interface. * The DataOffer gets created by DataDevice. * * @see DataOfferManager **/ class KWAYLANDCLIENT_EXPORT DataOffer : public QObject { Q_OBJECT public: virtual ~DataOffer(); /** * Releases the wl_data_offer interface. * After the interface has been released the DataOffer instance is no * longer valid and can be setup with another wl_data_offer interface. **/ void release(); /** * Destroys the data held by this DataOffer. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new wl_data_offer interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * DataOffer gets destroyed. * * @see release **/ void destroy(); /** * @returns @c true if managing a wl_data_offer. **/ bool isValid() const; QList offeredMimeTypes() const; void receive(const QMimeType &mimeType, qint32 fd); void receive(const QString &mimeType, qint32 fd); + /** + * Notifies the compositor that the drag destination successfully + * finished the drag-and-drop operation. + * + * After this operation it is only allowed to release the DataOffer. + * + * @since 5.42 + **/ + void dragAndDropFinished(); + + /** + * The actions offered by the DataSource. + * @since 5.42 + * @see sourceDragAndDropActionsChanged + **/ + DataDeviceManager::DnDActions sourceDragAndDropActions() const; + + /** + * Sets the @p supported and @p preferred Drag and Drop actions. + * @since 5.42 + **/ + void setDragAndDropActions(DataDeviceManager::DnDActions supported, DataDeviceManager::DnDAction preferred); + + /** + * The currently selected drag and drop action by the compositor. + * @see selectedDragAndDropActionChanged + * @since 5.42 + **/ + DataDeviceManager::DnDAction selectedDragAndDropAction() const; + operator wl_data_offer*(); operator wl_data_offer*() const; Q_SIGNALS: void mimeTypeOffered(const QString&); + /** + * Emitted whenever the @link{sourceDragAndDropActions} changed, e.g. on enter or when + * the DataSource changes the supported actions. + * @see sourceDragAndDropActions + * @since 5.42 + **/ + void sourceDragAndDropActionsChanged(); + /** + * Emitted whenever the selected drag and drop action changes. + * @see selectedDragAndDropAction + * @since 5.42 + **/ + void selectedDragAndDropActionChanged(); private: friend class DataDevice; explicit DataOffer(DataDevice *parent, wl_data_offer *dataOffer); class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::DataOffer*) #endif diff --git a/src/client/datasource.cpp b/src/client/datasource.cpp index def785b..615fe8c 100644 --- a/src/client/datasource.cpp +++ b/src/client/datasource.cpp @@ -1,145 +1,218 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "datasource.h" #include "wayland_pointer_p.h" // Qt #include // Wayland #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN DataSource::Private { public: explicit Private(DataSource *q); void setup(wl_data_source *s); WaylandPointer source; + DataDeviceManager::DnDAction selectedAction = DataDeviceManager::DnDAction::None; private: + void setAction(DataDeviceManager::DnDAction action); static void targetCallback(void *data, wl_data_source *dataSource, const char *mimeType); static void sendCallback(void *data, wl_data_source *dataSource, const char *mimeType, int32_t fd); static void cancelledCallback(void *data, wl_data_source *dataSource); + static void dndDropPerformedCallback(void *data, wl_data_source *wl_data_source); + static void dndFinishedCallback(void *data, wl_data_source *wl_data_source); + static void actionCallback(void *data, wl_data_source *wl_data_source, uint32_t dnd_action); static const struct wl_data_source_listener s_listener; DataSource *q; }; const wl_data_source_listener DataSource::Private::s_listener = { targetCallback, sendCallback, - cancelledCallback + cancelledCallback, + dndDropPerformedCallback, + dndFinishedCallback, + actionCallback }; DataSource::Private::Private(DataSource *q) : q(q) { } void DataSource::Private::targetCallback(void *data, wl_data_source *dataSource, const char *mimeType) { auto d = reinterpret_cast(data); Q_ASSERT(d->source == dataSource); emit d->q->targetAccepts(QString::fromUtf8(mimeType)); } void DataSource::Private::sendCallback(void *data, wl_data_source *dataSource, const char *mimeType, int32_t fd) { auto d = reinterpret_cast(data); Q_ASSERT(d->source == dataSource); emit d->q->sendDataRequested(QString::fromUtf8(mimeType), fd); } void DataSource::Private::cancelledCallback(void *data, wl_data_source *dataSource) { auto d = reinterpret_cast(data); Q_ASSERT(d->source == dataSource); emit d->q->cancelled(); } +void DataSource::Private::dndDropPerformedCallback(void *data, wl_data_source *wl_data_source) +{ + Q_UNUSED(wl_data_source) + auto d = reinterpret_cast(data); + emit d->q->dragAndDropPerformed(); +} + +void DataSource::Private::dndFinishedCallback(void *data, wl_data_source *wl_data_source) +{ + Q_UNUSED(wl_data_source) + auto d = reinterpret_cast(data); + emit d->q->dragAndDropFinished(); +} + +void DataSource::Private::actionCallback(void *data, wl_data_source *wl_data_source, uint32_t dnd_action) +{ + Q_UNUSED(wl_data_source) + auto d = reinterpret_cast(data); + switch(dnd_action) { + case WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY: + d->setAction(DataDeviceManager::DnDAction::Copy); + break; + case WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE: + d->setAction(DataDeviceManager::DnDAction::Move); + break; + case WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK: + d->setAction(DataDeviceManager::DnDAction::Ask); + break; + case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE: + d->setAction(DataDeviceManager::DnDAction::None); + break; + default: + Q_UNREACHABLE(); + } +} + +void DataSource::Private::setAction(DataDeviceManager::DnDAction action) +{ + if (action == selectedAction) { + return; + } + selectedAction = action; + emit q->selectedDragAndDropActionChanged(); +} + void DataSource::Private::setup(wl_data_source *s) { Q_ASSERT(!source.isValid()); Q_ASSERT(s); source.setup(s); wl_data_source_add_listener(s, &s_listener, this); } DataSource::DataSource(QObject *parent) : QObject(parent) , d(new Private(this)) { } DataSource::~DataSource() { release(); } void DataSource::release() { d->source.release(); } void DataSource::destroy() { d->source.destroy(); } bool DataSource::isValid() const { return d->source.isValid(); } void DataSource::setup(wl_data_source *dataSource) { d->setup(dataSource); } void DataSource::offer(const QString &mimeType) { wl_data_source_offer(d->source, mimeType.toUtf8().constData()); } void DataSource::offer(const QMimeType &mimeType) { if (!mimeType.isValid()) { return; } offer(mimeType.name()); } DataSource::operator wl_data_source*() const { return d->source; } DataSource::operator wl_data_source*() { return d->source; } +void DataSource::setDragAndDropActions(DataDeviceManager::DnDActions actions) +{ + uint32_t wlActions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + if (actions.testFlag(DataDeviceManager::DnDAction::Copy)) { + wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + } + if (actions.testFlag(DataDeviceManager::DnDAction::Move)) { + wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + } + if (actions.testFlag(DataDeviceManager::DnDAction::Ask)) { + wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; + } + wl_data_source_set_actions(d->source, wlActions); +} + +DataDeviceManager::DnDAction DataSource::selectedDragAndDropAction() const +{ + return d->selectedAction; +} + } } diff --git a/src/client/datasource.h b/src/client/datasource.h index 66fb005..d38a885 100644 --- a/src/client/datasource.h +++ b/src/client/datasource.h @@ -1,117 +1,174 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #ifndef WAYLAND_DATASOURCE_H #define WAYLAND_DATASOURCE_H #include "buffer.h" +#include "datadevicemanager.h" #include #include struct wl_data_source; class QMimeType; namespace KWayland { namespace Client { /** * @short Wrapper for the wl_data_source interface. * * This class is a convenient wrapper for the wl_data_source interface. * To create a DataSource call DataDeviceManager::createDataSource. * * @see DataDeviceManager **/ class KWAYLANDCLIENT_EXPORT DataSource : public QObject { Q_OBJECT public: explicit DataSource(QObject *parent = nullptr); virtual ~DataSource(); /** * Setup this DataSource to manage the @p dataSource. * When using DataDeviceManager::createDataSource there is no need to call this * method. **/ void setup(wl_data_source *dataSource); /** * Releases the wl_data_source interface. * After the interface has been released the DataSource instance is no * longer valid and can be setup with another wl_data_source interface. **/ void release(); /** * Destroys the data held by this DataSource. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new wl_data_source interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * DataSource gets destroyed. * * @see release **/ void destroy(); /** * @returns @c true if managing a wl_data_source. **/ bool isValid() const; void offer(const QString &mimeType); void offer(const QMimeType &mimeType); + /** + * Sets the actions that the source side client supports for this + * operation. + * + * This request must be made once only, and can only be made on sources + * used in drag-and-drop, so it must be performed before + * @link{DataDevice::startDrag}. Attempting to use the source other than + * for drag-and-drop will raise a protocol error. + * @since 5.42 + **/ + void setDragAndDropActions(DataDeviceManager::DnDActions actions); + + /** + * The currently selected drag and drop action by the compositor. + * @see selectedDragAndDropActionChanged + * @since 5.42 + **/ + DataDeviceManager::DnDAction selectedDragAndDropAction() const; + operator wl_data_source*(); operator wl_data_source*() const; Q_SIGNALS: /** * Emitted when a target accepts pointer_focus or motion events. If * a target does not accept any of the offered types, @p mimeType is empty. **/ void targetAccepts(const QString &mimeType); /** * Request for data from the client. Send the data as the * specified @p mimeType over the passed file descriptor @p fd, then close * it. **/ void sendDataRequested(const QString &mimeType, qint32 fd); /** * This DataSource has been replaced by another DataSource. * The client should clean up and destroy this DataSource. **/ void cancelled(); + /** + * The drag-and-drop operation physically finished. + * + * The user performed the drop action. This signal does not + * indicate acceptance, @link{cancelled} may still be + * emitted afterwards if the drop destination does not accept any + * mime type. + * + * However, this signal might not be received if the + * compositor cancelled the drag-and-drop operation before this + * signal could happen. + * + * Note that the DataSource may still be used in the future and + * should not be destroyed here. + * @since 5.42 + **/ + void dragAndDropPerformed(); + + /** + * The drag-and-drop operation concluded. + * + * The drop destination finished interoperating with this DataSource, + * so the client is now free to destroy this DataSource. + * + * If the action used to perform the operation was "move", the + * source can now delete the transferred data. + * @since 5.42 + */ + void dragAndDropFinished(); + + /** + * Emitted whenever the selected drag and drop action changes. + * @see selectedDragAndDropAction + * @since 5.42 + **/ + void selectedDragAndDropActionChanged(); + private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/idleinhibit.cpp b/src/client/idleinhibit.cpp new file mode 100644 index 0000000..ae21320 --- /dev/null +++ b/src/client/idleinhibit.cpp @@ -0,0 +1,176 @@ +/**************************************************************************** +Copyright 2017 Martin Flöser + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#include "idleinhibit.h" +#include "event_queue.h" +#include "surface.h" +#include "wayland_pointer_p.h" + +#include + +namespace KWayland +{ +namespace Client +{ + +class Q_DECL_HIDDEN IdleInhibitManager::Private +{ +public: + Private() = default; + + void setup(zwp_idle_inhibit_manager_v1 *arg); + + WaylandPointer idleinhibitmanager; + EventQueue *queue = nullptr; +}; + +IdleInhibitManager::IdleInhibitManager(QObject *parent) + : QObject(parent) + , d(new Private) +{ +} + +void IdleInhibitManager::Private::setup(zwp_idle_inhibit_manager_v1 *arg) +{ + Q_ASSERT(arg); + Q_ASSERT(!idleinhibitmanager); + idleinhibitmanager.setup(arg); +} + +IdleInhibitManager::~IdleInhibitManager() +{ + release(); +} + +void IdleInhibitManager::setup(zwp_idle_inhibit_manager_v1 *idleinhibitmanager) +{ + d->setup(idleinhibitmanager); +} + +void IdleInhibitManager::release() +{ + d->idleinhibitmanager.release(); +} + +void IdleInhibitManager::destroy() +{ + d->idleinhibitmanager.destroy(); +} + +IdleInhibitManager::operator zwp_idle_inhibit_manager_v1*() { + return d->idleinhibitmanager; +} + +IdleInhibitManager::operator zwp_idle_inhibit_manager_v1*() const { + return d->idleinhibitmanager; +} + +bool IdleInhibitManager::isValid() const +{ + return d->idleinhibitmanager.isValid(); +} + +void IdleInhibitManager::setEventQueue(EventQueue *queue) +{ + d->queue = queue; +} + +EventQueue *IdleInhibitManager::eventQueue() +{ + return d->queue; +} + +IdleInhibitor *IdleInhibitManager::createInhibitor(Surface *surface, QObject *parent) +{ + Q_ASSERT(isValid()); + auto p = new IdleInhibitor(parent); + auto w = zwp_idle_inhibit_manager_v1_create_inhibitor(d->idleinhibitmanager, *surface); + if (d->queue) { + d->queue->addProxy(w); + } + p->setup(w); + return p; +} + +class Q_DECL_HIDDEN IdleInhibitor::Private +{ +public: + Private(IdleInhibitor *q); + + void setup(zwp_idle_inhibitor_v1 *arg); + + WaylandPointer idleinhibitor; + +private: + IdleInhibitor *q; +}; + +IdleInhibitor::Private::Private(IdleInhibitor *q) + : q(q) +{ +} + +IdleInhibitor::IdleInhibitor(QObject *parent) + : QObject(parent) + , d(new Private(this)) +{ +} + +void IdleInhibitor::Private::setup(zwp_idle_inhibitor_v1 *arg) +{ + Q_ASSERT(arg); + Q_ASSERT(!idleinhibitor); + idleinhibitor.setup(arg); +} + +IdleInhibitor::~IdleInhibitor() +{ + release(); +} + +void IdleInhibitor::setup(zwp_idle_inhibitor_v1 *idleinhibitor) +{ + d->setup(idleinhibitor); +} + +void IdleInhibitor::release() +{ + d->idleinhibitor.release(); +} + +void IdleInhibitor::destroy() +{ + d->idleinhibitor.destroy(); +} + +IdleInhibitor::operator zwp_idle_inhibitor_v1*() { + return d->idleinhibitor; +} + +IdleInhibitor::operator zwp_idle_inhibitor_v1*() const { + return d->idleinhibitor; +} + +bool IdleInhibitor::isValid() const +{ + return d->idleinhibitor.isValid(); +} + +} +} diff --git a/src/client/idleinhibit.h b/src/client/idleinhibit.h new file mode 100644 index 0000000..f538a13 --- /dev/null +++ b/src/client/idleinhibit.h @@ -0,0 +1,217 @@ +/**************************************************************************** +Copyright 2017 Martin Flöser + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#ifndef KWAYLAND_CLIENT_IDLEINHIBIT_H +#define KWAYLAND_CLIENT_IDLEINHIBIT_H + +#include + +#include + +struct zwp_idle_inhibit_manager_v1; +struct zwp_idle_inhibitor_v1; + +namespace KWayland +{ +namespace Client +{ + +class EventQueue; +class Surface; +class IdleInhibitor; + +/** + * @short Wrapper for the zwp_idle_inhibit_manager_v1 interface. + * + * This class provides a convenient wrapper for the zwp_idle_inhibit_manager_v1 interface. + * + * To use this class one needs to interact with the Registry. There are two + * possible ways to create the IdleInhibitManager interface: + * @code + * IdleInhibitManager *c = registry->createIdleInhibitManager(name, version); + * @endcode + * + * This creates the IdleInhibitManager and sets it up directly. As an alternative this + * can also be done in a more low level way: + * @code + * IdleInhibitManager *c = new IdleInhibitManager; + * c->setup(registry->bindIdleInhibitManager(name, version)); + * @endcode + * + * The IdleInhibitManager can be used as a drop-in replacement for any zwp_idle_inhibit_manager_v1 + * pointer as it provides matching cast operators. + * + * @see Registry + * @since 5.41 + **/ +class KWAYLANDCLIENT_EXPORT IdleInhibitManager : public QObject +{ + Q_OBJECT +public: + /** + * Creates a new IdleInhibitManager. + * Note: after constructing the IdleInhibitManager it is not yet valid and one needs + * to call setup. In order to get a ready to use IdleInhibitManager prefer using + * Registry::createIdleInhibitManager. + **/ + explicit IdleInhibitManager(QObject *parent = nullptr); + virtual ~IdleInhibitManager(); + + /** + * Setup this IdleInhibitManager to manage the @p idleinhibitmanager. + * When using Registry::createIdleInhibitManager there is no need to call this + * method. + **/ + void setup(zwp_idle_inhibit_manager_v1 *idleinhibitmanager); + /** + * @returns @c true if managing a zwp_idle_inhibit_manager_v1. + **/ + bool isValid() const; + /** + * Releases the zwp_idle_inhibit_manager_v1 interface. + * After the interface has been released the IdleInhibitManager instance is no + * longer valid and can be setup with another zwp_idle_inhibit_manager_v1 interface. + **/ + void release(); + /** + * Destroys the data held by this IdleInhibitManager. + * This method is supposed to be used when the connection to the Wayland + * server goes away. If the connection is not valid anymore, it's not + * possible to call release anymore as that calls into the Wayland + * connection and the call would fail. This method cleans up the data, so + * that the instance can be deleted or set up to a new zwp_idle_inhibit_manager_v1 interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, idleinhibitmanager, &IdleInhibitManager::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + /** + * Sets the @p queue to use for creating objects with this IdleInhibitManager. + **/ + void setEventQueue(EventQueue *queue); + /** + * @returns The event queue to use for creating objects with this IdleInhibitManager. + **/ + EventQueue *eventQueue(); + + /** + * Creates an IdleInhibitor for the given @p surface. + * While the IdleInhibitor exists the @p surface is marked to inhibit idle. + * @param surface The Surface which should have idle inhibited + * @param parent The parent object for the IdleInhibitor + * @returns The created IdleInhibitor + **/ + IdleInhibitor *createInhibitor(Surface *surface, QObject *parent = nullptr); + + operator zwp_idle_inhibit_manager_v1*(); + operator zwp_idle_inhibit_manager_v1*() const; + +Q_SIGNALS: + /** + * The corresponding global for this interface on the Registry got removed. + * + * This signal gets only emitted if the IdleInhibitManager got created by + * Registry::createIdleInhibitManager + **/ + void removed(); + +private: + class Private; + QScopedPointer d; +}; + +/** + * An IdleInhibitor prevents the Output that the associated Surface is visible on from being + * set to a state where it is not visually usable due to lack of user interaction + * (e.g. blanked, dimmed, locked, set to power save, etc.) Any screensaver processes are + * also blocked from displaying. + * + * If the Surface is destroyed, unmapped, becomes occluded, loses visibility, or otherwise + * becomes not visually relevant for the user, the IdleInhibitor will not be honored by + * the compositor; if the Surface subsequently regains visibility the inhibitor takes effect + * once again. + * Likewise, the IdleInhibitor isn't honored if the system was already idled at the time the + * IdleInhibitor was established, although if the system later de-idles and re-idles the + * IdleInhibitor will take effect. + * + * @see IdleInhibitManager + * @see Surface + * @since 5.41 + **/ +class KWAYLANDCLIENT_EXPORT IdleInhibitor : public QObject +{ + Q_OBJECT +public: + virtual ~IdleInhibitor(); + + /** + * Setup this IdleInhibitor to manage the @p idleinhibitor. + * When using IdleInhibitManager::createIdleInhibitor there is no need to call this + * method. + **/ + void setup(zwp_idle_inhibitor_v1 *idleinhibitor); + /** + * @returns @c true if managing a zwp_idle_inhibitor_v1. + **/ + bool isValid() const; + /** + * Releases the zwp_idle_inhibitor_v1 interface. + * After the interface has been released the IdleInhibitor instance is no + * longer valid and can be setup with another zwp_idle_inhibitor_v1 interface. + **/ + void release(); + /** + * Destroys the data held by this IdleInhibitor. + * This method is supposed to be used when the connection to the Wayland + * server goes away. If the connection is not valid anymore, it's not + * possible to call release anymore as that calls into the Wayland + * connection and the call would fail. This method cleans up the data, so + * that the instance can be deleted or set up to a new zwp_idle_inhibitor_v1 interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, idleinhibitor, &IdleInhibitor::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + operator zwp_idle_inhibitor_v1*(); + operator zwp_idle_inhibitor_v1*() const; + +private: + friend class IdleInhibitManager; + explicit IdleInhibitor(QObject *parent = nullptr); + class Private; + QScopedPointer d; +}; + + +} +} + +#endif diff --git a/src/client/protocols/appmenu.xml b/src/client/protocols/appmenu.xml new file mode 100644 index 0000000..2483f72 --- /dev/null +++ b/src/client/protocols/appmenu.xml @@ -0,0 +1,47 @@ + + + . + ]]> + + + This interface allows a client to link a window (or wl_surface) to an com.canonical.dbusmenu + interface registered on DBus. + + + + + + + + + The DBus service name and object path where the appmenu interface is present + The object should be registered on the session bus before sending this request. + If not applicable, clients should remove this object. + + + + Set or update the service name and object path. + Strings should be formatted in Latin-1 matching the relevant DBus specifications. + + + + + + + + + diff --git a/src/client/protocols/idle-inhibit-unstable-v1.xml b/src/client/protocols/idle-inhibit-unstable-v1.xml new file mode 100644 index 0000000..9c06cdc --- /dev/null +++ b/src/client/protocols/idle-inhibit-unstable-v1.xml @@ -0,0 +1,83 @@ + + + + + Copyright © 2015 Samsung Electronics Co., Ltd + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + + This interface permits inhibiting the idle behavior such as screen + blanking, locking, and screensaving. The client binds the idle manager + globally, then creates idle-inhibitor objects for each surface. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + Destroy the inhibit manager. + + + + + + Create a new inhibitor object associated with the given surface. + + + + + + + + + + An idle inhibitor prevents the output that the associated surface is + visible on from being set to a state where it is not visually usable due + to lack of user interaction (e.g. blanked, dimmed, locked, set to power + save, etc.) Any screensaver processes are also blocked from displaying. + + If the surface is destroyed, unmapped, becomes occluded, loses + visibility, or otherwise becomes not visually relevant for the user, the + idle inhibitor will not be honored by the compositor; if the surface + subsequently regains visibility the inhibitor takes effect once again. + Likewise, the inhibitor isn't honored if the system was already idled at + the time the inhibitor was established, although if the system later + de-idles and re-idles the inhibitor will take effect. + + + + + Remove the inhibitor effect from the associated wl_surface. + + + + + diff --git a/src/client/protocols/server-decoration-palette.xml b/src/client/protocols/server-decoration-palette.xml new file mode 100644 index 0000000..26e50ac --- /dev/null +++ b/src/client/protocols/server-decoration-palette.xml @@ -0,0 +1,44 @@ + + + . + ]]> + +l + This interface allows a client to alter the palette of a server side decoration. + + + + + + + + + This interface allows a client to alter the palette of a server side decoration. + + + + Color scheme that should be applied to the window decoration. + Absolute file path, or name of palette in the user's config directory. + The server may choose not to follow the requested style. + + + + + + + + diff --git a/src/client/registry.cpp b/src/client/registry.cpp index 439f21e..65fa65a 100644 --- a/src/client/registry.cpp +++ b/src/client/registry.cpp @@ -1,795 +1,837 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "registry.h" #include "compositor.h" #include "connection_thread.h" #include "datadevicemanager.h" #include "dpms.h" #include "event_queue.h" #include "fakeinput.h" #include "fullscreen_shell.h" #include "idle.h" +#include "idleinhibit.h" #include "remote_access.h" #include "logging_p.h" #include "outputconfiguration.h" #include "outputmanagement.h" #include "outputdevice.h" #include "output.h" #include "plasmashell.h" #include "plasmawindowmanagement.h" #include "pointerconstraints.h" #include "pointergestures.h" #include "seat.h" #include "shadow.h" #include "blur.h" #include "contrast.h" #include "relativepointer.h" #include "server_decoration.h" #include "slide.h" #include "shell.h" #include "shm_pool.h" #include "subcompositor.h" #include "textinput_p.h" #include "xdgshell.h" #include "xdgshell_p.h" #include "wayland_pointer_p.h" #include "xdgforeign_v2.h" +#include "appmenu.h" +#include "server_decoration_palette.h" // Qt #include // wayland #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include +#include /***** * How to add another interface: * * define a new enum value in Registry::Interface * * define the bind method * * define the create method * * define the Announced signal * * define the Removed signal * * add a block to s_interfaces * * add the BIND macro for the new bind * * add the CREATE macro for the new create * * extend registry unit test to verify that it works ****/ namespace KWayland { namespace Client { namespace { struct SuppertedInterfaceData { quint32 maxVersion; QByteArray name; const wl_interface *interface; void (Registry::*announcedSignal)(quint32, quint32); void (Registry::*removedSignal)(quint32); }; static const QMap s_interfaces = { {Registry::Interface::Compositor, { 3, QByteArrayLiteral("wl_compositor"), &wl_compositor_interface, &Registry::compositorAnnounced, &Registry::compositorRemoved }}, {Registry::Interface::DataDeviceManager, { - 2, + 3, QByteArrayLiteral("wl_data_device_manager"), &wl_data_device_manager_interface, &Registry::dataDeviceManagerAnnounced, &Registry::dataDeviceManagerRemoved }}, {Registry::Interface::Output, { 2, QByteArrayLiteral("wl_output"), &wl_output_interface, &Registry::outputAnnounced, &Registry::outputRemoved }}, {Registry::Interface::Shm, { 1, QByteArrayLiteral("wl_shm"), &wl_shm_interface, &Registry::shmAnnounced, &Registry::shmRemoved }}, {Registry::Interface::Seat, { 4, QByteArrayLiteral("wl_seat"), &wl_seat_interface, &Registry::seatAnnounced, &Registry::seatRemoved }}, {Registry::Interface::Shell, { 1, QByteArrayLiteral("wl_shell"), &wl_shell_interface, &Registry::shellAnnounced, &Registry::shellRemoved }}, {Registry::Interface::SubCompositor, { 1, QByteArrayLiteral("wl_subcompositor"), &wl_subcompositor_interface, &Registry::subCompositorAnnounced, &Registry::subCompositorRemoved }}, {Registry::Interface::PlasmaShell, { 4, QByteArrayLiteral("org_kde_plasma_shell"), &org_kde_plasma_shell_interface, &Registry::plasmaShellAnnounced, &Registry::plasmaShellRemoved }}, {Registry::Interface::PlasmaWindowManagement, { 7, QByteArrayLiteral("org_kde_plasma_window_management"), &org_kde_plasma_window_management_interface, &Registry::plasmaWindowManagementAnnounced, &Registry::plasmaWindowManagementRemoved }}, {Registry::Interface::Idle, { 1, QByteArrayLiteral("org_kde_kwin_idle"), &org_kde_kwin_idle_interface, &Registry::idleAnnounced, &Registry::idleRemoved }}, {Registry::Interface::RemoteAccessManager, { 1, QByteArrayLiteral("org_kde_kwin_remote_access_manager"), &org_kde_kwin_remote_access_manager_interface, &Registry::remoteAccessManagerAnnounced, &Registry::remoteAccessManagerRemoved }}, {Registry::Interface::FakeInput, { 2, QByteArrayLiteral("org_kde_kwin_fake_input"), &org_kde_kwin_fake_input_interface, &Registry::fakeInputAnnounced, &Registry::fakeInputRemoved }}, {Registry::Interface::OutputManagement, { 1, QByteArrayLiteral("org_kde_kwin_outputmanagement"), &org_kde_kwin_outputmanagement_interface, &Registry::outputManagementAnnounced, &Registry::outputManagementRemoved }}, {Registry::Interface::OutputDevice, { 1, QByteArrayLiteral("org_kde_kwin_outputdevice"), &org_kde_kwin_outputdevice_interface, &Registry::outputDeviceAnnounced, &Registry::outputDeviceRemoved }}, {Registry::Interface::Shadow, { 2, QByteArrayLiteral("org_kde_kwin_shadow_manager"), &org_kde_kwin_shadow_manager_interface, &Registry::shadowAnnounced, &Registry::shadowRemoved }}, {Registry::Interface::Blur, { 1, QByteArrayLiteral("org_kde_kwin_blur_manager"), &org_kde_kwin_blur_manager_interface, &Registry::blurAnnounced, &Registry::blurRemoved }}, {Registry::Interface::Contrast, { 1, QByteArrayLiteral("org_kde_kwin_contrast_manager"), &org_kde_kwin_contrast_manager_interface, &Registry::contrastAnnounced, &Registry::contrastRemoved }}, {Registry::Interface::Slide, { 1, QByteArrayLiteral("org_kde_kwin_slide_manager"), &org_kde_kwin_slide_manager_interface, &Registry::slideAnnounced, &Registry::slideRemoved }}, {Registry::Interface::FullscreenShell, { 1, QByteArrayLiteral("_wl_fullscreen_shell"), &_wl_fullscreen_shell_interface, &Registry::fullscreenShellAnnounced, &Registry::fullscreenShellRemoved }}, {Registry::Interface::Dpms, { 1, QByteArrayLiteral("org_kde_kwin_dpms_manager"), &org_kde_kwin_dpms_manager_interface, &Registry::dpmsAnnounced, &Registry::dpmsRemoved }}, {Registry::Interface::ServerSideDecorationManager, { 1, QByteArrayLiteral("org_kde_kwin_server_decoration_manager"), &org_kde_kwin_server_decoration_manager_interface, &Registry::serverSideDecorationManagerAnnounced, &Registry::serverSideDecorationManagerRemoved }}, {Registry::Interface::TextInputManagerUnstableV0, { 1, QByteArrayLiteral("wl_text_input_manager"), &wl_text_input_manager_interface, &Registry::textInputManagerUnstableV0Announced, &Registry::textInputManagerUnstableV0Removed }}, {Registry::Interface::TextInputManagerUnstableV2, { 1, QByteArrayLiteral("zwp_text_input_manager_v2"), &zwp_text_input_manager_v2_interface, &Registry::textInputManagerUnstableV2Announced, &Registry::textInputManagerUnstableV2Removed }}, {Registry::Interface::XdgShellUnstableV5, { 1, QByteArrayLiteral("xdg_shell"), &xdg_shell_interface, &Registry::xdgShellUnstableV5Announced, &Registry::xdgShellUnstableV5Removed }}, {Registry::Interface::RelativePointerManagerUnstableV1, { 1, QByteArrayLiteral("zwp_relative_pointer_manager_v1"), &zwp_relative_pointer_manager_v1_interface, &Registry::relativePointerManagerUnstableV1Announced, &Registry::relativePointerManagerUnstableV1Removed }}, {Registry::Interface::PointerGesturesUnstableV1, { 1, QByteArrayLiteral("zwp_pointer_gestures_v1"), &zwp_pointer_gestures_v1_interface, &Registry::pointerGesturesUnstableV1Announced, &Registry::pointerGesturesUnstableV1Removed }}, {Registry::Interface::PointerConstraintsUnstableV1, { 1, QByteArrayLiteral("zwp_pointer_constraints_v1"), &zwp_pointer_constraints_v1_interface, &Registry::pointerConstraintsUnstableV1Announced, &Registry::pointerConstraintsUnstableV1Removed }}, {Registry::Interface::XdgExporterUnstableV2, { 1, QByteArrayLiteral("zxdg_exporter_v2"), &zxdg_exporter_v2_interface, &Registry::exporterUnstableV2Announced, &Registry::exporterUnstableV2Removed }}, {Registry::Interface::XdgImporterUnstableV2, { 1, QByteArrayLiteral("zxdg_importer_v2"), &zxdg_importer_v2_interface, &Registry::importerUnstableV2Announced, &Registry::importerUnstableV2Removed }}, {Registry::Interface::XdgShellUnstableV6, { 1, QByteArrayLiteral("zxdg_shell_v6"), &zxdg_shell_v6_interface, &Registry::xdgShellUnstableV6Announced, &Registry::xdgShellUnstableV6Removed + }}, + {Registry::Interface::IdleInhibitManagerUnstableV1, { + 1, + QByteArrayLiteral("zwp_idle_inhibit_manager_v1"), + &zwp_idle_inhibit_manager_v1_interface, + &Registry::idleInhibitManagerUnstableV1Announced, + &Registry::idleInhibitManagerUnstableV1Removed + }}, + {Registry::Interface::AppMenu, { + 1, + QByteArrayLiteral("org_kde_kwin_appmenu_manager"), + &org_kde_kwin_appmenu_manager_interface, + &Registry::appMenuAnnounced, + &Registry::appMenuRemoved + }}, + {Registry::Interface::ServerSideDecorationPalette, { + 1, + QByteArrayLiteral("org_kde_kwin_server_decoration_palette_manager"), + &org_kde_kwin_server_decoration_palette_manager_interface, + &Registry::serverSideDecorationPaletteManagerAnnounced, + &Registry::serverSideDecorationPaletteManagerRemoved }} }; static quint32 maxVersion(const Registry::Interface &interface) { auto it = s_interfaces.find(interface); if (it != s_interfaces.end()) { return it.value().maxVersion; } return 0; } } class Q_DECL_HIDDEN Registry::Private { public: Private(Registry *q); void setup(); bool hasInterface(Interface interface) const; AnnouncedInterface interface(Interface interface) const; QVector interfaces(Interface interface) const; Interface interfaceForName(quint32 name) const; template T *bind(Interface interface, uint32_t name, uint32_t version) const; template T *create(quint32 name, quint32 version, QObject *parent, WL *(Registry::*bindMethod)(uint32_t, uint32_t) const); WaylandPointer registry; static const struct wl_callback_listener s_callbackListener; WaylandPointer callback; EventQueue *queue = nullptr; private: void handleAnnounce(uint32_t name, const char *interface, uint32_t version); void handleRemove(uint32_t name); void handleGlobalSync(); static void globalAnnounce(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version); static void globalRemove(void *data, struct wl_registry *registry, uint32_t name); static void globalSync(void *data, struct wl_callback *callback, uint32_t serial); Registry *q; struct InterfaceData { Interface interface; uint32_t name; uint32_t version; }; QList m_interfaces; static const struct wl_registry_listener s_registryListener; }; Registry::Private::Private(Registry *q) : q(q) { } void Registry::Private::setup() { wl_registry_add_listener(registry, &s_registryListener, this); wl_callback_add_listener(callback, &s_callbackListener, this); } Registry::Registry(QObject *parent) : QObject(parent) , d(new Private(this)) { } Registry::~Registry() { release(); } void Registry::release() { d->registry.release(); d->callback.release(); } void Registry::destroy() { emit registryDestroyed(); d->registry.destroy(); d->callback.destroy(); } void Registry::create(wl_display *display) { Q_ASSERT(display); Q_ASSERT(!isValid()); d->registry.setup(wl_display_get_registry(display)); d->callback.setup(wl_display_sync(display)); if (d->queue) { d->queue->addProxy(d->registry); d->queue->addProxy(d->callback); } } void Registry::create(ConnectionThread *connection) { create(connection->display()); connect(connection, &ConnectionThread::connectionDied, this, &Registry::destroy); } void Registry::setup() { Q_ASSERT(isValid()); d->setup(); } void Registry::setEventQueue(EventQueue *queue) { d->queue = queue; if (!queue) { return; } if (d->registry) { d->queue->addProxy(d->registry); } if (d->callback) { d->queue->addProxy(d->callback); } } EventQueue *Registry::eventQueue() { return d->queue; } #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct wl_registry_listener Registry::Private::s_registryListener = { globalAnnounce, globalRemove }; const struct wl_callback_listener Registry::Private::s_callbackListener = { globalSync }; #endif void Registry::Private::globalAnnounce(void *data, wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { auto r = reinterpret_cast(data); Q_ASSERT(registry == r->registry); r->handleAnnounce(name, interface, version); } void Registry::Private::globalRemove(void *data, wl_registry *registry, uint32_t name) { auto r = reinterpret_cast(data); Q_ASSERT(registry == r->registry); r->handleRemove(name); } void Registry::Private::globalSync(void* data, wl_callback* callback, uint32_t serial) { Q_UNUSED(serial) auto r = reinterpret_cast(data); Q_ASSERT(r->callback == callback); r->handleGlobalSync(); r->callback.destroy(); } void Registry::Private::handleGlobalSync() { emit q->interfacesAnnounced(); } namespace { static Registry::Interface nameToInterface(const char *interface) { for (auto it = s_interfaces.begin(); it != s_interfaces.end(); ++it) { if (qstrcmp(interface, it.value().name) == 0) { return it.key(); } } return Registry::Interface::Unknown; } } void Registry::Private::handleAnnounce(uint32_t name, const char *interface, uint32_t version) { Interface i = nameToInterface(interface); emit q->interfaceAnnounced(QByteArray(interface), name, version); if (i == Interface::Unknown) { qCDebug(KWAYLAND_CLIENT) << "Unknown interface announced: " << interface << "/" << name << "/" << version; return; } qCDebug(KWAYLAND_CLIENT) << "Wayland Interface: " << interface << "/" << name << "/" << version; m_interfaces.append({i, name, version}); auto it = s_interfaces.constFind(i); if (it != s_interfaces.end()) { emit (q->*it.value().announcedSignal)(name, version); } } void Registry::Private::handleRemove(uint32_t name) { auto it = std::find_if(m_interfaces.begin(), m_interfaces.end(), [name](const InterfaceData &data) { return data.name == name; } ); if (it != m_interfaces.end()) { InterfaceData data = *(it); m_interfaces.erase(it); auto sit = s_interfaces.find(data.interface); if (sit != s_interfaces.end()) { emit (q->*sit.value().removedSignal)(data.name); } } emit q->interfaceRemoved(name); } bool Registry::Private::hasInterface(Registry::Interface interface) const { auto it = std::find_if(m_interfaces.begin(), m_interfaces.end(), [interface](const InterfaceData &data) { return data.interface == interface; } ); return it != m_interfaces.end(); } QVector Registry::Private::interfaces(Interface interface) const { QVector retVal; for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) { const auto &data = *it; if (data.interface == interface) { retVal << AnnouncedInterface{data.name, data.version}; } } return retVal; } Registry::AnnouncedInterface Registry::Private::interface(Interface interface) const { const auto all = interfaces(interface); if (!all.isEmpty()) { return all.last(); } return AnnouncedInterface{0, 0}; } Registry::Interface Registry::Private::interfaceForName(quint32 name) const { auto it = std::find_if(m_interfaces.constBegin(), m_interfaces.constEnd(), [name] (const InterfaceData &data) { return data.name == name; }); if (it == m_interfaces.constEnd()) { return Interface::Unknown; } return (*it).interface; } bool Registry::hasInterface(Registry::Interface interface) const { return d->hasInterface(interface); } QVector Registry::interfaces(Interface interface) const { return d->interfaces(interface); } Registry::AnnouncedInterface Registry::interface(Interface interface) const { return d->interface(interface); } #define BIND2(__NAME__, __INAME__, __WL__) \ __WL__ *Registry::bind##__NAME__(uint32_t name, uint32_t version) const \ { \ return d->bind<__WL__>(Interface::__INAME__, name, qMin(maxVersion(Interface::__INAME__), version)); \ } #define BIND(__NAME__, __WL__) BIND2(__NAME__, __NAME__, __WL__) BIND(Compositor, wl_compositor) BIND(Output, wl_output) BIND(Seat, wl_seat) BIND(Shell, wl_shell) BIND(Shm, wl_shm) BIND(SubCompositor, wl_subcompositor) BIND(FullscreenShell, _wl_fullscreen_shell) BIND(DataDeviceManager, wl_data_device_manager) BIND(PlasmaShell, org_kde_plasma_shell) BIND(PlasmaWindowManagement, org_kde_plasma_window_management) BIND(Idle, org_kde_kwin_idle) BIND(RemoteAccessManager, org_kde_kwin_remote_access_manager) BIND(FakeInput, org_kde_kwin_fake_input) BIND(OutputManagement, org_kde_kwin_outputmanagement) BIND(OutputDevice, org_kde_kwin_outputdevice) BIND(ServerSideDecorationManager, org_kde_kwin_server_decoration_manager) BIND(TextInputManagerUnstableV0, wl_text_input_manager) BIND(TextInputManagerUnstableV2, zwp_text_input_manager_v2) BIND(XdgShellUnstableV5, xdg_shell) BIND(XdgShellUnstableV6, zxdg_shell_v6) BIND(RelativePointerManagerUnstableV1, zwp_relative_pointer_manager_v1) BIND(PointerGesturesUnstableV1, zwp_pointer_gestures_v1) BIND(PointerConstraintsUnstableV1, zwp_pointer_constraints_v1) BIND(XdgExporterUnstableV2, zxdg_exporter_v2) BIND(XdgImporterUnstableV2, zxdg_importer_v2) +BIND(IdleInhibitManagerUnstableV1, zwp_idle_inhibit_manager_v1) BIND2(ShadowManager, Shadow, org_kde_kwin_shadow_manager) BIND2(BlurManager, Blur, org_kde_kwin_blur_manager) BIND2(ContrastManager, Contrast, org_kde_kwin_contrast_manager) BIND2(SlideManager, Slide, org_kde_kwin_slide_manager) BIND2(DpmsManager, Dpms, org_kde_kwin_dpms_manager) +BIND2(AppMenuManager, AppMenu, org_kde_kwin_appmenu_manager) +BIND2(ServerSideDecorationPaletteManager, ServerSideDecorationPalette, org_kde_kwin_server_decoration_palette_manager) #undef BIND #undef BIND2 template T *Registry::Private::create(quint32 name, quint32 version, QObject *parent, WL *(Registry::*bindMethod)(uint32_t, uint32_t) const) { T *t = new T(parent); t->setEventQueue(queue); t->setup((q->*bindMethod)(name, version)); QObject::connect(q, &Registry::interfaceRemoved, t, [t, name] (quint32 removed) { if (name == removed) { emit t->removed(); } } ); QObject::connect(q, &Registry::registryDestroyed, t, &T::destroy); return t; } #define CREATE2(__NAME__, __BINDNAME__) \ __NAME__ *Registry::create##__NAME__(quint32 name, quint32 version, QObject *parent) \ { \ return d->create<__NAME__>(name, version, parent, &Registry::bind##__BINDNAME__); \ } #define CREATE(__NAME__) CREATE2(__NAME__, __NAME__) CREATE(Compositor) CREATE(Seat) CREATE(Shell) CREATE(SubCompositor) CREATE(FullscreenShell) CREATE(Output) CREATE(DataDeviceManager) CREATE(PlasmaShell) CREATE(PlasmaWindowManagement) CREATE(Idle) CREATE(RemoteAccessManager) CREATE(FakeInput) CREATE(OutputManagement) CREATE(OutputDevice) CREATE(ShadowManager) CREATE(BlurManager) CREATE(ContrastManager) CREATE(SlideManager) CREATE(DpmsManager) CREATE(ServerSideDecorationManager) CREATE2(ShmPool, Shm) +CREATE(AppMenuManager) +CREATE(ServerSideDecorationPaletteManager) #undef CREATE #undef CREATE2 XdgExporter *Registry::createXdgExporter(quint32 name, quint32 version, QObject *parent) { //only V1 supported for now return d->create(name, version, parent, &Registry::bindXdgExporterUnstableV2); } XdgImporter *Registry::createXdgImporter(quint32 name, quint32 version, QObject *parent) { //only V1 supported for now return d->create(name, version, parent, &Registry::bindXdgImporterUnstableV2); } TextInputManager *Registry::createTextInputManager(quint32 name, quint32 version, QObject *parent) { switch (d->interfaceForName(name)) { case Interface::TextInputManagerUnstableV0: return d->create(name, version, parent, &Registry::bindTextInputManagerUnstableV0); case Interface::TextInputManagerUnstableV2: return d->create(name, version, parent, &Registry::bindTextInputManagerUnstableV2); default: return nullptr; } } XdgShell *Registry::createXdgShell(quint32 name, quint32 version, QObject *parent) { switch (d->interfaceForName(name)) { case Interface::XdgShellUnstableV5: return d->create(name, version, parent, &Registry::bindXdgShellUnstableV5); case Interface::XdgShellUnstableV6: return d->create(name, version, parent, &Registry::bindXdgShellUnstableV6); default: return nullptr; } } RelativePointerManager *Registry::createRelativePointerManager(quint32 name, quint32 version, QObject *parent) { switch (d->interfaceForName(name)) { case Interface::RelativePointerManagerUnstableV1: return d->create(name, version, parent, &Registry::bindRelativePointerManagerUnstableV1); default: return nullptr; } } PointerGestures *Registry::createPointerGestures(quint32 name, quint32 version, QObject *parent) { switch (d->interfaceForName(name)) { case Interface::PointerGesturesUnstableV1: return d->create(name, version, parent, &Registry::bindPointerGesturesUnstableV1); default: return nullptr; } } PointerConstraints *Registry::createPointerConstraints(quint32 name, quint32 version, QObject *parent) { switch (d->interfaceForName(name)) { case Interface::PointerConstraintsUnstableV1: return d->create(name, version, parent, &Registry::bindPointerConstraintsUnstableV1); default: return nullptr; } } +IdleInhibitManager *Registry::createIdleInhibitManager(quint32 name, quint32 version, QObject *parent) +{ + switch (d->interfaceForName(name)) { + case Interface::IdleInhibitManagerUnstableV1: + return d->create(name, version, parent, &Registry::bindIdleInhibitManagerUnstableV1); + default: + return nullptr; + } +} + namespace { static const wl_interface *wlInterface(Registry::Interface interface) { auto it = s_interfaces.find(interface); if (it != s_interfaces.end()) { return it.value().interface; } return nullptr; } } template T *Registry::Private::bind(Registry::Interface interface, uint32_t name, uint32_t version) const { auto it = std::find_if(m_interfaces.begin(), m_interfaces.end(), [=](const InterfaceData &data) { return data.interface == interface && data.name == name && data.version >= version; }); if (it == m_interfaces.end()) { qCDebug(KWAYLAND_CLIENT) << "Don't have interface " << int(interface) << "with name " << name << "and minimum version" << version; return nullptr; } auto t = reinterpret_cast(wl_registry_bind(registry, name, wlInterface(interface), version)); if (queue) { queue->addProxy(t); } return t; } bool Registry::isValid() const { return d->registry.isValid(); } wl_registry *Registry::registry() { return d->registry; } Registry::operator wl_registry*() const { return d->registry; } Registry::operator wl_registry*() { return d->registry; } } } diff --git a/src/client/registry.h b/src/client/registry.h index a2dee60..48bdfe4 100644 --- a/src/client/registry.h +++ b/src/client/registry.h @@ -1,1475 +1,1614 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #ifndef WAYLAND_REGISTRY_H #define WAYLAND_REGISTRY_H #include #include #include struct wl_compositor; struct wl_data_device_manager; struct wl_display; struct wl_output; struct wl_registry; struct wl_seat; struct wl_shell; struct wl_shm; struct wl_subcompositor; struct wl_text_input_manager; struct zwp_text_input_manager_v2; struct _wl_fullscreen_shell; +struct org_kde_kwin_appmenu_manager; struct org_kde_kwin_outputmanagement; struct org_kde_kwin_outputdevice; struct org_kde_kwin_fake_input; struct org_kde_kwin_idle; struct org_kde_kwin_remote_access_manager; struct org_kde_kwin_dpms_manager; struct org_kde_kwin_shadow_manager; struct org_kde_kwin_blur_manager; struct org_kde_kwin_contrast_manager; struct org_kde_kwin_slide_manager; struct org_kde_plasma_shell; struct org_kde_plasma_window_management; struct org_kde_kwin_server_decoration_manager; +struct org_kde_kwin_server_decoration_palette_manager; struct xdg_shell; struct zxdg_shell_v6; struct zwp_relative_pointer_manager_v1; struct zwp_pointer_gestures_v1; struct zwp_pointer_constraints_v1; struct zxdg_exporter_v2; struct zxdg_importer_v2; +struct zwp_idle_inhibit_manager_v1; namespace KWayland { namespace Client { +class AppMenuManager; class Compositor; class ConnectionThread; class DataDeviceManager; class DpmsManager; class EventQueue; class FakeInput; class FullscreenShell; class OutputManagement; class OutputDevice; class Idle; +class IdleInhibitManager; class RemoteAccessManager; class Output; class PlasmaShell; class PlasmaWindowManagement; class PointerConstraints; class PointerGestures; class Seat; class ShadowManager; class BlurManager; class ContrastManager; class SlideManager; class Shell; class ShmPool; class ServerSideDecorationManager; +class ServerSideDecorationPaletteManager; class SubCompositor; class TextInputManager; class TextInputManagerUnstableV0; class TextInputManagerUnstableV2; class XdgShell; class RelativePointerManager; class XdgExporterUnstableV2; class XdgImporterUnstableV2; class XdgExporter; class XdgImporter; /** * @short Wrapper for the wl_registry interface. * * The purpose of this class is to manage the wl_registry interface. * This class supports some well-known interfaces and can create a * wrapper class for those. * * The main purpose is to emit signals whenever a new interface is * added or an existing interface is removed. For the well known interfaces * dedicated signals are emitted allowing a user to connect directly to the * signal announcing the interface it is interested in. * * To create and setup the Registry one needs to call create with either a * wl_display from an existing Wayland connection or a ConnectionThread instance: * * @code * ConnectionThread *connection; // existing connection * Registry registry; * registry.create(connection); * registry.setup(); * @endcode * * The interfaces are announced in an asynchronous way by the Wayland server. * To initiate the announcing of the interfaces one needs to call setup. **/ class KWAYLANDCLIENT_EXPORT Registry : public QObject { Q_OBJECT public: /** * The well-known interfaces this Registry supports. * For each of the enum values the Registry is able to create a Wrapper * object. **/ enum class Interface { Unknown, ///< Refers to an Unknown interface Compositor, ///< Refers to the wl_compositor interface Shell, ///< Refers to the wl_shell interface Seat, ///< Refers to the wl_seat interface Shm, ///< Refers to the wl_shm interface Output, ///< Refers to the wl_output interface FullscreenShell, ///< Refers to the _wl_fullscreen_shell interface SubCompositor, ///< Refers to the wl_subcompositor interface; DataDeviceManager, ///< Refers to the wl_data_device_manager interface PlasmaShell, ///< Refers to org_kde_plasma_shell interface PlasmaWindowManagement, ///< Refers to org_kde_plasma_window_management interface Idle, ///< Refers to org_kde_kwin_idle_interface interface FakeInput, ///< Refers to org_kde_kwin_fake_input interface Shadow, ///< Refers to org_kde_kwin_shadow_manager interface Blur, ///< refers to org_kde_kwin_blur_manager interface Contrast, ///< refers to org_kde_kwin_contrast_manager interface Slide, ///< refers to org_kde_kwin_slide_manager Dpms, ///< Refers to org_kde_kwin_dpms_manager interface OutputManagement, ///< Refers to the wl_data_device_manager interface OutputDevice, ///< Refers to the org_kde_kwin_outputdevice interface ServerSideDecorationManager, ///< Refers to org_kde_kwin_server_decoration_manager TextInputManagerUnstableV0, ///< Refers to wl_text_input_manager, @since 5.23 TextInputManagerUnstableV2, ///< Refers to zwp_text_input_manager_v2, @since 5.23 XdgShellUnstableV5, ///< Refers to xdg_shell (unstable version 5), @since 5.25 RelativePointerManagerUnstableV1, ///< Refers to zwp_relative_pointer_manager_v1, @since 5.28 PointerGesturesUnstableV1, ///< Refers to zwp_pointer_gestures_v1, @since 5.29 PointerConstraintsUnstableV1, ///< Refers to zwp_pointer_constraints_v1, @since 5.29 XdgExporterUnstableV2, ///< refers to zxdg_exporter_v2, @since 5.40 XdgImporterUnstableV2, ///< refers to zxdg_importer_v2, @since 5.40 - XdgShellUnstableV6, ///< Refers to zxdg_shell_v6 (unstable version 6), @since 5.XX - RemoteAccessManager ///< Refers to org_kde_kwin_remote_access_manager interface, @since 5.40 + XdgShellUnstableV6, ///< Refers to zxdg_shell_v6 (unstable version 6), @since 5.39 + IdleInhibitManagerUnstableV1, ///< Refers to zwp_idle_inhibit_manager_v1 (unstable version 1), @since 5.41 + AppMenu, ///Refers to org_kde_kwin_appmenu @since 5.42 + ServerSideDecorationPalette, ///Refers to org_kde_kwin_server_decoration_palette_manager @since 5.42 + RemoteAccessManager ///< Refers to org_kde_kwin_remote_access_manager interface, @since 5.43 }; explicit Registry(QObject *parent = nullptr); virtual ~Registry(); /** * Releases the wl_registry interface. * After the interface has been released the Registry instance is no * longer valid and can be setup with another wl_registry interface. **/ void release(); /** * Destroys the data held by this Registry. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new wl_registry interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, registry, &Registry::destroy); * @endcode * * @see release **/ void destroy(); /** * Gets the registry from the @p display. **/ void create(wl_display *display); /** * Gets the registry from the @p connection. **/ void create(ConnectionThread *connection); /** * Finalizes the setup of the Registry. * After calling this method the interfaces will be announced in an asynchronous way. * The Registry must have been created when calling this method. * @see create **/ void setup(); /** * Sets the @p queue to use for this Registry. * * The EventQueue should be set before the Registry gets setup. * The EventQueue gets automatically added to all interfaces created by * this Registry. So that all objects are in teh same EventQueue. * * @param queue The event queue to use for this Registry. **/ void setEventQueue(EventQueue *queue); /** * @returns The EventQueue used by this Registry **/ EventQueue *eventQueue(); /** * @returns @c true if managing a wl_registry. **/ bool isValid() const; /** * @returns @c true if the Registry has an @p interface. **/ bool hasInterface(Interface interface) const; /** * Representation of one announced interface. **/ struct AnnouncedInterface { /** * The name of the announced interface. **/ quint32 name; /** * The maximum supported version of the announced interface. **/ quint32 version; }; /** * Provides name and version for the @p interface. * * The first value of the returned pair is the "name", the second value is the "version". * If the @p interface has not been announced, both values are set to 0. * If there @p interface has been announced multiple times, the last announced is returned. * In case one is interested in all announced interfaces, one should prefer @link interfaces(Interface) @endlink. * * The returned information can be passed into the bind or create methods. * * @param interface The well-known interface for which the name and version should be retrieved * @returns name and version of the given interface * @since 5.5 **/ AnnouncedInterface interface(Interface interface) const; /** * Provides all pairs of name and version for the well-known @p interface. * * If the @p interface has not been announced, an empty vector is returned. * * The returned information can be passed into the bind or create methods. * * @param interface The well-known interface for which the name and version should be retrieved * @returns All pairs of name and version of the given interface * @since 5.5 **/ QVector interfaces(Interface interface) const; /** * @name Low-level bind methods for global interfaces. **/ ///@{ /** * Binds the wl_compositor with @p name and @p version. * If the @p name does not exist or is not for the compositor interface, * @c null will be returned. * * Prefer using createCompositor instead. * @see createCompositor **/ wl_compositor *bindCompositor(uint32_t name, uint32_t version) const; /** * Binds the wl_shell with @p name and @p version. * If the @p name does not exist or is not for the shell interface, * @c null will be returned. * * Prefer using createShell instead. * @see createShell **/ wl_shell *bindShell(uint32_t name, uint32_t version) const; /** * Binds the wl_seat with @p name and @p version. * If the @p name does not exist or is not for the seat interface, * @c null will be returned. * * Prefer using createSeat instead. * @see createSeat **/ wl_seat *bindSeat(uint32_t name, uint32_t version) const; /** * Binds the wl_shm with @p name and @p version. * If the @p name does not exist or is not for the shm interface, * @c null will be returned. * * Prefer using createShmPool instead. * @see createShmPool **/ wl_shm *bindShm(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_outputmanagement with @p name and @p version. * If the @p name does not exist or is not for the outputmanagement interface, * @c null will be returned. * * Prefer using createOutputManagement instead. * @see createOutputManagement **/ org_kde_kwin_outputmanagement *bindOutputManagement(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_outputdevice with @p name and @p version. * If the @p name does not exist or is not for the outputdevice interface, * @c null will be returned. * * Prefer using createOutputDevice instead. * @see createOutputDevice **/ wl_output *bindOutput(uint32_t name, uint32_t version) const; /** * Binds the wl_subcompositor with @p name and @p version. * If the @p name does not exist or is not for the subcompositor interface, * @c null will be returned. * * Prefer using createSubCompositor instead. * @see createSubCompositor **/ wl_subcompositor *bindSubCompositor(uint32_t name, uint32_t version) const; /** * Binds the wl_output with @p name and @p version. * If the @p name does not exist or is not for the output interface, * @c null will be returned. * * Prefer using createOutput instead. * @see createOutput * @since 5.5 **/ org_kde_kwin_outputdevice *bindOutputDevice(uint32_t name, uint32_t version) const; /** * Binds the _wl_fullscreen_shell with @p name and @p version. * If the @p name does not exist or is not for the fullscreen shell interface, * @c null will be returned. * * Prefer using createFullscreenShell instead. * @see createFullscreenShell **/ _wl_fullscreen_shell *bindFullscreenShell(uint32_t name, uint32_t version) const; /** * Binds the wl_data_device_manager with @p name and @p version. * If the @p name does not exist or is not for the data device manager interface, * @c null will be returned. * * Prefer using createDataDeviceManager instead. * @see createDataDeviceManager **/ wl_data_device_manager *bindDataDeviceManager(uint32_t name, uint32_t version) const; /** * Binds the org_kde_plasma_shell with @p name and @p version. * If the @p name does not exist or is not for the Plasma shell interface, * @c null will be returned. * * Prefer using createPlasmaShell instead. * @see createPlasmaShell * @since 5.4 **/ org_kde_plasma_shell *bindPlasmaShell(uint32_t name, uint32_t version) const; /** * Binds the org_kde_plasma_window_management with @p name and @p version. * If the @p name does not exist or is not for the Plasma window management interface, * @c null will be returned. * * Prefer using createPlasmaWindowManagement instead. * @see createPlasmaWindowManagement * @since 5.4 **/ org_kde_plasma_window_management *bindPlasmaWindowManagement(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_idle with @p name and @p version. * If the @p name does not exist or is not for the idle interface, * @c null will be returned. * * Prefer using createIdle instead. * @see createIdle * @since 5.4 **/ org_kde_kwin_idle *bindIdle(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_remote_access_manager with @p name and @p version. * If the @p name does not exist or is not for the idle interface, * @c null will be returned. * * Prefer using createRemoteAccessManager instead. * @see createRemoteAccessManager * @since 5.23 **/ org_kde_kwin_remote_access_manager *bindRemoteAccessManager(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_fake_input with @p name and @p version. * If the @p name does not exist or is not for the fake input interface, * @c null will be returned. * * Prefer using createFakeInput instead. * @see createFakeInput * @since 5.4 **/ org_kde_kwin_fake_input *bindFakeInput(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_shadow_manager with @p name and @p version. * If the @p name does not exist or is not for the shadow manager interface, * @c null will be returned. * * Prefer using createShadowManager instead. * @see createShadowManager * @since 5.4 **/ org_kde_kwin_shadow_manager *bindShadowManager(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_blur_manager with @p name and @p version. * If the @p name does not exist or is not for the blur manager interface, * @c null will be returned. * * Prefer using createBlurManager instead. * @see createBlurManager * @since 5.5 **/ org_kde_kwin_blur_manager *bindBlurManager(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_contrast_manager with @p name and @p version. * If the @p name does not exist or is not for the contrast manager interface, * @c null will be returned. * * Prefer using createContrastManager instead. * @see createContrastManager * @since 5.5 **/ org_kde_kwin_contrast_manager *bindContrastManager(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_slide_manager with @p name and @p version. * If the @p name does not exist or is not for the slide manager interface, * @c null will be returned. * * Prefer using createSlideManager instead. * @see createSlideManager * @since 5.5 **/ org_kde_kwin_slide_manager * bindSlideManager(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_dpms_manager with @p name and @p version. * If the @p name does not exist or is not for the dpms manager interface, * @c null will be returned. * * Prefer using createDpmsManager instead. * @see createDpmsManager * @since 5.5 **/ org_kde_kwin_dpms_manager *bindDpmsManager(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_server_decoration_manager with @p name and @p version. * If the @p name does not exist or is not for the server side decoration manager interface, * @c null will be returned. * * Prefer using createServerSideDecorationManager instead. * @see createServerSideDecorationManager * @since 5.6 **/ org_kde_kwin_server_decoration_manager *bindServerSideDecorationManager(uint32_t name, uint32_t version) const; /** * Binds the wl_text_input_manager with @p name and @p version. * If the @p name does not exist or is not for the text input interface in unstable version 0, * @c null will be returned. * * Prefer using createTextInputManager instead. * @see createTextInputManager * @since 5.23 **/ wl_text_input_manager *bindTextInputManagerUnstableV0(uint32_t name, uint32_t version) const; /** * Binds the zwp_text_input_manager_v2 with @p name and @p version. * If the @p name does not exist or is not for the text input interface in unstable version 2, * @c null will be returned. * * Prefer using createTextInputManager instead. * @see createTextInputManager * @since 5.23 **/ zwp_text_input_manager_v2 *bindTextInputManagerUnstableV2(uint32_t name, uint32_t version) const; /** * Binds the xdg_shell (unstable version 5) with @p name and @p version. * If the @p name does not exist or is not for the xdg shell interface in unstable version 5, * @c null will be returned. * * Prefer using createXdgShell instead. * @see createXdgShell * @since 5.25 **/ xdg_shell *bindXdgShellUnstableV5(uint32_t name, uint32_t version) const; /** * Binds the zxdg_shell_v6 (unstable version 6) with @p name and @p version. * If the @p name does not exist or is not for the xdg shell interface in unstable version 5, * @c null will be returned. * * Prefer using createXdgShell instead. * @see createXdgShell * @since 5.39 **/ zxdg_shell_v6 *bindXdgShellUnstableV6(uint32_t name, uint32_t version) const; /** * Binds the zwp_relative_pointer_manager_v1 with @p name and @p version. * If the @p name does not exist or is not for the relative pointer interface in unstable version 1, * @c null will be returned. * * Prefer using createRelativePointerManager instead. * @see createRelativePointerManager * @since 5.28 **/ zwp_relative_pointer_manager_v1 *bindRelativePointerManagerUnstableV1(uint32_t name, uint32_t version) const; /** * Binds the zwp_pointer_gestures_v1 with @p name and @p version. * If the @p name does not exist or is not for the pointer gestures interface in unstable version 1, * @c null will be returned. * * Prefer using createPointerGestures instead. * @see createPointerGestures * @since 5.29 **/ zwp_pointer_gestures_v1 *bindPointerGesturesUnstableV1(uint32_t name, uint32_t version) const; /** * Binds the zwp_pointer_constraints_v1 with @p name and @p version. * If the @p name does not exist or is not for the pointer constraints interface in unstable version 1, * @c null will be returned. * * Prefer using createPointerConstraints instead. * @see createPointerConstraints * @since 5.29 **/ zwp_pointer_constraints_v1 *bindPointerConstraintsUnstableV1(uint32_t name, uint32_t version) const; /** * Binds the zxdg_exporter_v2 with @p name and @p version. * If the @p name does not exists or isnot for the exporter * extension in unstable version 1, * @c null will be returned. * * Prefer using createXdgExporter * @since 5.40 */ zxdg_exporter_v2 *bindXdgExporterUnstableV2(uint32_t name, uint32_t version) const; /** * Binds the zxdg_importer_v2 with @p name and @p version. * If the @p name does not exists or isnot for the importer * extension in unstable version 1, * @c null will be returned. * * Prefer using createXdgImporter * @since 5.40 */ zxdg_importer_v2 *bindXdgImporterUnstableV2(uint32_t name, uint32_t version) const; + + /** + * Binds the zwp_idle_inhibit_manager_v1 with @p name and @p version. + * If the @p name does not exists or is not for the idle inhibit manager in unstable version 1, + * @c null will be returned. + * + * Prefer using createIdleInhibitManager + * @since 5.41 + */ + zwp_idle_inhibit_manager_v1 *bindIdleInhibitManagerUnstableV1(uint32_t name, uint32_t version) const; + + /** + * Binds the org_kde_kwin_appmenu_manager with @p name and @p version. + * If the @p name does not exist or is not for the appmenu manager interface, + * @c null will be returned. + * + * Prefer using createAppMenuManager instead. + * @see createAppMenuManager + * @since 5.42 + **/ + org_kde_kwin_appmenu_manager *bindAppMenuManager(uint32_t name, uint32_t version) const; + + /** + * Binds the org_kde_kwin_server_decoration_palette_manager with @p name and @p version. + * If the @p name does not exist or is not for the server side decoration palette manager interface, + * @c null will be returned. + * + * Prefer using createServerSideDecorationPaletteManager instead. + * @see createAppMenuManager + * @since 5.42 + **/ + org_kde_kwin_server_decoration_palette_manager *bindServerSideDecorationPaletteManager(uint32_t name, uint32_t version) const; ///@} /** * @name Convenient factory methods for global objects. **/ ///@{ /** * Creates a Compositor and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the wl_compositor interface, * the returned Compositor will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the wl_compositor interface to bind * @param version The version or the wl_compositor interface to use * @param parent The parent for Compositor * * @returns The created Compositor. **/ Compositor *createCompositor(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a Seat and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the wl_seat interface, * the returned Seat will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the wl_seat interface to bind * @param version The version or the wl_seat interface to use * @param parent The parent for Seat * * @returns The created Seat. **/ Shell *createShell(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a Compositor and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the wl_compositor interface, * the returned Compositor will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the wl_compositor interface to bind * @param version The version or the wl_compositor interface to use * @param parent The parent for Compositor * * @returns The created Compositor. **/ Seat *createSeat(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a ShmPool and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the wl_shm interface, * the returned ShmPool will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the wl_shm interface to bind * @param version The version or the wl_shm interface to use * @param parent The parent for ShmPool * * @returns The created ShmPool. **/ ShmPool *createShmPool(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a SubCompositor and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the wl_subcompositor interface, * the returned SubCompositor will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the wl_subcompositor interface to bind * @param version The version or the wl_subcompositor interface to use * @param parent The parent for SubCompositor * * @returns The created SubCompositor. **/ SubCompositor *createSubCompositor(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates an Output and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the wl_output interface, * the returned Output will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the wl_output interface to bind * @param version The version or the wl_output interface to use * @param parent The parent for Output * * @returns The created Output. **/ Output *createOutput(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates an KWinOutputManagement and sets it up to manage the interface identified * by @p name and @p version. * * Note: in case @p name is invalid or isn't for the wl_output interface, * the returned KWinConnectors will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_outputmanagement interface to bind * @param version The version or the org_kde_kwin_outputmanagement interface to use * @param parent The parent for KWinOutputManagement * * @returns The created KWinOutputManagement. * @since 5.5 **/ OutputManagement *createOutputManagement(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates an OutputDevice and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_outputdevice interface, * the returned OutputDevice will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_outputdevice interface to bind * @param version The version or the org_kde_kwin_outputdevice interface to use * @param parent The parent for OutputDevice * * @returns The created Output. * @since 5.5 **/ OutputDevice *createOutputDevice(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a FullscreenShell and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the _wl_fullscreen_shell interface, * the returned FullscreenShell will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the _wl_fullscreen_shell interface to bind * @param version The version or the _wl_fullscreen_shell interface to use * @param parent The parent for FullscreenShell * * @returns The created FullscreenShell. * @since 5.5 **/ FullscreenShell *createFullscreenShell(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a DataDeviceManager and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the wl_data_device_manager interface, * the returned DataDeviceManager will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the wl_data_device_manager interface to bind * @param version The version or the wl_data_device_manager interface to use * @param parent The parent for DataDeviceManager * * @returns The created DataDeviceManager. **/ DataDeviceManager *createDataDeviceManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a PlasmaShell and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_plasma_shell interface, * the returned PlasmaShell will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_plasma_shell interface to bind * @param version The version or the org_kde_plasma_shell interface to use * @param parent The parent for PlasmaShell * * @returns The created PlasmaShell. * @since 5.4 **/ PlasmaShell *createPlasmaShell(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a PlasmaWindowManagement and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_plasma_window_management interface, * the returned PlasmaWindowManagement will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_plasma_window_management interface to bind * @param version The version or the org_kde_plasma_window_management interface to use * @param parent The parent for PlasmaWindowManagement * * @returns The created PlasmaWindowManagement. * @since 5.4 **/ PlasmaWindowManagement *createPlasmaWindowManagement(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates an Idle and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_idle interface, * the returned Idle will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_idle interface to bind * @param version The version or the org_kde_kwin_idle interface to use * @param parent The parent for Idle * * @returns The created Idle. * @since 5.4 **/ Idle *createIdle(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a RemoteAccessManager and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_remote_access_manager interface, * the returned RemoteAccessManager will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_remote_access_manager interface to bind * @param version The version or the org_kde_kwin_remote_access_manager interface to use * @param parent The parent for RemoteAccessManager * * @returns The created RemoteAccessManager. * @since 5.23 **/ RemoteAccessManager *createRemoteAccessManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a FakeInput and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_fake_input interface, * the returned FakeInput will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_fake_input interface to bind * @param version The version or the org_kde_kwin_fake_input interface to use * @param parent The parent for FakeInput * * @returns The created FakeInput. * @since 5.4 **/ FakeInput *createFakeInput(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a ShadowManager and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_shadow_manager interface, * the returned ShadowManager will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_shadow_manager interface to bind * @param version The version or the org_kde_kwin_shadow_manager interface to use * @param parent The parent for ShadowManager * * @returns The created ShadowManager. * @since 5.4 **/ ShadowManager *createShadowManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a BlurManager and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_blur_manager interface, * the returned BlurManager will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_blur_manager interface to bind * @param version The version or the org_kde_kwin_blur_manager interface to use * @param parent The parent for BlurManager * * @returns The created BlurManager. * @since 5.5 **/ BlurManager *createBlurManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a ContrastManager and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_contrast_manager interface, * the returned ContrastManager will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_contrast_manager interface to bind * @param version The version or the org_kde_kwin_contrast_manager interface to use * @param parent The parent for ContrastManager * * @returns The created ContrastManager. * @since 5.5 **/ ContrastManager *createContrastManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a SlideManager and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_slide_manager interface, * the returned SlideManager will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_slide_manager interface to bind * @param version The version or the org_kde_kwin_slide_manager interface to use * @param parent The parent for SlideManager * * @returns The created SlideManager. * @since 5.5 **/ SlideManager *createSlideManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a DpmsManager and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_dpms_manager interface, * the returned DpmsManager will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_dpms_manager interface to bind * @param version The version or the org_kde_kwin_dpms_manager interface to use * @param parent The parent for DpmsManager * * @returns The created DpmsManager. * @since 5.5 **/ DpmsManager *createDpmsManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a ServerSideDecorationManager and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_server_decoration_manager interface, * the returned ServerSideDecorationManager will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_server_decoration_manager interface to bind * @param version The version or the org_kde_kwin_server_decoration_manager interface to use * @param parent The parent for ServerSideDecorationManager * * @returns The created ServerSideDecorationManager. * @since 5.6 **/ ServerSideDecorationManager *createServerSideDecorationManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a TextInputManager and sets it up to manage the interface identified by * @p name and @p version. * * This factory method supports the following interfaces: * @li wl_text_input_manager * @li zwp_text_input_manager_v2 * * If @p name is for one of the supported interfaces the corresponding manager will be created, * otherwise @c null will be returned. * * @param name The name of the interface to bind * @param version The version of the interface to use * @param parent The parent for the TextInputManager * * @returns The created TextInputManager * @since 5.23 **/ TextInputManager *createTextInputManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates an XdgShell and sets it up to manage the interface identified by * @p name and @p version. * * This factory method supports the following interfaces: * @li xdg_shell (Unstable version 5) * * If @p name is for one of the supported interfaces the corresponding shell will be created, * otherwise @c null will be returned. * * @param name The name of the interface to bind * @param version The version of the interface to use * @param parent The parent for the XdgShell * * @returns The created XdgShell * @since 5.25 **/ XdgShell *createXdgShell(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a RelativePointerManager and sets it up to manage the interface identified by * @p name and @p version. * * This factory method supports the following interfaces: * @li zwp_relative_pointer_manager_v1 * * If @p name is for one of the supported interfaces the corresponding manager will be created, * otherwise @c null will be returned. * * @param name The name of the interface to bind * @param version The version of the interface to use * @param parent The parent for the RelativePointerManager * * @returns The created RelativePointerManager * @since 5.28 **/ RelativePointerManager *createRelativePointerManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a PointerGestures and sets it up to manage the interface identified by * @p name and @p version. * * This factory method supports the following interfaces: * @li zwp_pointer_gestures_v1 * * If @p name is for one of the supported interfaces the corresponding manager will be created, * otherwise @c null will be returned. * * @param name The name of the interface to bind * @param version The version of the interface to use * @param parent The parent for the PointerGestures * * @returns The created PointerGestures * @since 5.29 **/ PointerGestures *createPointerGestures(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a PointerConstraints and sets it up to manage the interface identified by * @p name and @p version. * * This factory method supports the following interfaces: * @li zwp_pointer_constraints_v1 * * If @p name is for one of the supported interfaces the corresponding manager will be created, * otherwise @c null will be returned. * * @param name The name of the interface to bind * @param version The version of the interface to use * @param parent The parent for the PointerConstraints * * @returns The created PointerConstraints * @since 5.29 **/ PointerConstraints *createPointerConstraints(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates an XdgExporter and sets it up to manage the interface identified by * @p name and @p version. * * This factory method supports the following interfaces: * @li zxdg_exporter_v2 * * If @p name is for one of the supported interfaces the corresponding manager will be created, * otherwise @c null will be returned. * * @returns The created XdgExporter * @since 5.40 */ XdgExporter *createXdgExporter(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates an XdgImporter and sets it up to manage the interface identified by * @p name and @p version. * * This factory method supports the following interfaces: * @li zxdg_importer_v2 * * If @p name is for one of the supported interfaces the corresponding manager will be created, * otherwise @c null will be returned. * * @returns The created XdgImporter * @since 5.40 */ XdgImporter *createXdgImporter(quint32 name, quint32 version, QObject *parent = nullptr); + + /** + * Creates an IdleInhibitManager and sets it up to manage the interface identified by + * @p name and @p version. + * + * This factory method supports the following interfaces: + * @li zwp_idle_inhibit_manager_v1 + * + * If @p name is for one of the supported interfaces the corresponding manager will be created, + * otherwise @c null will be returned. + * + * @returns The created IdleInhibitManager + * @since 5.41 + */ + IdleInhibitManager *createIdleInhibitManager(quint32 name, quint32 version, QObject *parent = nullptr); + + /** + * Creates a AppMenuManager and sets it up to manage the interface identified by + * @p name and @p version. + * + * Note: in case @p name is invalid or isn't for the org_kde_kwin_appmenu_manager interface, + * the returned AppMenuManager will not be valid. Therefore it's recommended to call + * isValid on the created instance. + * + * @param name The name of the org_kde_kwin_appmenu_manager interface to bind + * @param version The version or the org_kde_kwin_appmenu_manager interface to use + * @param parent The parent for AppMenuManager + * + * @returns The created AppMenuManager. + * @since 5.42 + **/ + AppMenuManager *createAppMenuManager(quint32 name, quint32 version, QObject *parent = nullptr); + + /** + * Creates a ServerSideDecorationPaletteManager and sets it up to manage the interface identified by + * @p name and @p version. + * + * Note: in case @p name is invalid or isn't for the org_kde_kwin_appmenu_manager interface, + * the returned ServerSideDecorationPaletteManager will not be valid. Therefore it's recommended to call + * isValid on the created instance. + * + * @param name The name of the org_kde_kwin_server_decoration_palette_manager interface to bind + * @param version The version or the org_kde_kwin_server_decoration_palette_manager interface to use + * @param parent The parent for ServerSideDecorationPaletteManager + * + * @returns The created ServerSideDecorationPaletteManager. + * @since 5.42 + **/ + ServerSideDecorationPaletteManager *createServerSideDecorationPaletteManager(quint32 name, quint32 version, QObject *parent = nullptr); ///@} + /** * cast operator to the low-level Wayland @c wl_registry **/ operator wl_registry*(); /** * cast operator to the low-level Wayland @c wl_registry **/ operator wl_registry*() const; /** * @returns access to the low-level Wayland @c wl_registry **/ wl_registry *registry(); Q_SIGNALS: /** * @name Interface announced signals. **/ ///@{ /** * Emitted whenever a wl_compositor interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface **/ void compositorAnnounced(quint32 name, quint32 version); /** * Emitted whenever a wl_shell interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface **/ void shellAnnounced(quint32 name, quint32 version); /** * Emitted whenever a wl_seat interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface **/ void seatAnnounced(quint32 name, quint32 version); /** * Emitted whenever a wl_shm interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface **/ void shmAnnounced(quint32 name, quint32 version); /** * Emitted whenever a wl_subcompositor interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface **/ void subCompositorAnnounced(quint32 name, quint32 version); /** * Emitted whenever a wl_output interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface **/ void outputAnnounced(quint32 name, quint32 version); /** * Emitted whenever a _wl_fullscreen_shell interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface **/ void fullscreenShellAnnounced(quint32 name, quint32 version); /** * Emitted whenever a wl_data_device_manager interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface **/ void dataDeviceManagerAnnounced(quint32 name, quint32 version); void outputManagementAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_kwin_outputdevice interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.5 **/ void outputDeviceAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_plasma_shell interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.4 **/ void plasmaShellAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_plasma_window_management interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.4 **/ void plasmaWindowManagementAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_kwin_idle interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.4 **/ void idleAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_kwin_remote_access_manager interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.23 **/ void remoteAccessManagerAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_kwin_fake_input interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.4 **/ void fakeInputAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_kwin_shadow_manager interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.4 **/ void shadowAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_kwin_blur_manager interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.5 **/ void blurAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_kwin_contrast_manager interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.5 **/ void contrastAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_kwin_slide_manager interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.5 **/ void slideAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_kwin_dpms_manager interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.5 **/ void dpmsAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_kwin_server_decoration_manager interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.6 **/ void serverSideDecorationManagerAnnounced(quint32 name, quint32 version); /** * Emitted whenever a wl_text_input_manager interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.23 **/ void textInputManagerUnstableV0Announced(quint32 name, quint32 version); /** * Emitted whenever a zwp_text_input_manager_v2 interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.23 **/ void textInputManagerUnstableV2Announced(quint32 name, quint32 version); /** * Emitted whenever a xdg_shell (unstable version 5) interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.25 **/ void xdgShellUnstableV5Announced(quint32 name, quint32 version); /** * Emitted whenever a zxdg_shell_v6 (unstable version 6) interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.25 **/ void xdgShellUnstableV6Announced(quint32 name, quint32 version); /** * Emitted whenever a zwp_relative_pointer_manager_v1 interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.28 **/ void relativePointerManagerUnstableV1Announced(quint32 name, quint32 version); /** * Emitted whenever a zwp_pointer_gestures_v1 interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.29 **/ void pointerGesturesUnstableV1Announced(quint32 name, quint32 version); /** * Emitted whenever a zwp_pointer_constraints_v1 interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.29 **/ void pointerConstraintsUnstableV1Announced(quint32 name, quint32 version); /** * Emitted whenever a zxdg_exporter_v2 interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.40 */ void exporterUnstableV2Announced(quint32 name, quint32 version); /** * Emitted whenever a zxdg_importer_v2 interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.40 */ void importerUnstableV2Announced(quint32 name, quint32 version); + + /** + * Emitted whenever a zwp_idle_inhibit_manager_v1 interface gets announced. + * @param name The name for the announced interface + * @param version The maximum supported version of the announced interface + * @since 5.41 + */ + void idleInhibitManagerUnstableV1Announced(quint32 name, quint32 version); + + /** + * Emitted whenever a org_kde_kwin_appmenu_manager interface gets announced. + * @param name The name for the announced interface + * @param version The maximum supported version of the announced interface + * @since 5.42 + */ + void appMenuAnnounced(quint32 name, quint32 version); + + /** + * Emitted whenever a org_kde_kwin_server_decoration_palette_manager interface gets announced. + * @param name The name for the announced interface + * @param version The maximum supported version of the announced interface + * @since 5.42 + */ + void serverSideDecorationPaletteManagerAnnounced(quint32 name, quint32 version); + ///@} + /** * @name Interface removed signals. **/ ///@{ /** * Emitted whenever a wl_compositor interface gets removed. * @param name The name for the removed interface **/ void compositorRemoved(quint32 name); /** * Emitted whenever a wl_shell interface gets removed. * @param name The name for the removed interface **/ void shellRemoved(quint32 name); /** * Emitted whenever a wl_seat interface gets removed. * @param name The name for the removed interface **/ void seatRemoved(quint32 name); /** * Emitted whenever a wl_shm interface gets removed. * @param name The name for the removed interface **/ void shmRemoved(quint32 name); /** * Emitted whenever a wl_subcompositor interface gets removed. * @param name The name for the removed interface **/ void subCompositorRemoved(quint32 name); /** * Emitted whenever a wl_output interface gets removed. * @param name The name for the removed interface **/ void outputRemoved(quint32 name); /** * Emitted whenever a _wl_fullscreen_shell interface gets removed. * @param name The name for the removed interface **/ void fullscreenShellRemoved(quint32 name); /** * Emitted whenever a wl_data_device_manager interface gets removed. * @param name The name for the removed interface **/ void dataDeviceManagerRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_outputmanagement interface gets removed. * @param name The name for the removed interface * @since 5.5 **/ void outputManagementRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_outputdevice interface gets removed. * @param name The name for the removed interface * @since 5.5 **/ void outputDeviceRemoved(quint32 name); /** * Emitted whenever a org_kde_plasma_shell interface gets removed. * @param name The name for the removed interface * @since 5.4 **/ void plasmaShellRemoved(quint32 name); /** * Emitted whenever a org_kde_plasma_window_management interface gets removed. * @param name The name for the removed interface * @since 5.4 **/ void plasmaWindowManagementRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_idle interface gets removed. * @param name The name for the removed interface * @since 5.4 **/ void idleRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_remote_access_manager interface gets removed. * @param name The name for the removed interface * @since 5.23 **/ void remoteAccessManagerRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_fake_input interface gets removed. * @param name The name for the removed interface * @since 5.4 **/ void fakeInputRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_shadow_manager interface gets removed. * @param name The name for the removed interface * @since 5.4 **/ void shadowRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_blur_manager interface gets removed. * @param name The name for the removed interface * @since 5.5 **/ void blurRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_contrast_manager interface gets removed. * @param name The name for the removed interface * @since 5.5 **/ void contrastRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_slide_manager interface gets removed. * @param name The name for the removed interface * @since 5.5 **/ void slideRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_dpms_manager interface gets removed. * @param name The name for the removed interface * @since 5.5 **/ void dpmsRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_server_decoration_manager interface gets removed. * @param name The name for the removed interface * @since 5.6 **/ void serverSideDecorationManagerRemoved(quint32 name); /** * Emitted whenever a wl_text_input_manager interface gets removed. * @param name The name for the removed interface * @since 5.23 **/ void textInputManagerUnstableV0Removed(quint32 name); /** * Emitted whenever a zwp_text_input_manager_v2 interface gets removed. * @param name The name for the removed interface * @since 5.23 **/ void textInputManagerUnstableV2Removed(quint32 name); /** * Emitted whenever an xdg_shell (unstable version 5) interface gets removed. * @param name The name for the removed interface * @since 5.25 **/ void xdgShellUnstableV5Removed(quint32 name); /** * Emitted whenever an xdg_shell (unstable version 5) interface gets removed. * @param name The name for the removed interface * @since 5.25 **/ void xdgShellUnstableV6Removed(quint32 name); /** * Emitted whenever a zwp_relative_pointer_manager_v1 interface gets removed. * @param name The name for the removed interface * @since 5.28 **/ void relativePointerManagerUnstableV1Removed(quint32 name); /** * Emitted whenever a zwp_pointer_gestures_v1 interface gets removed. * @param name The name for the removed interface * @since 5.29 **/ void pointerGesturesUnstableV1Removed(quint32 name); /** * Emitted whenever a zwp_pointer_constraints_v1 interface gets removed. * @param name The name for the removed interface * @since 5.29 **/ void pointerConstraintsUnstableV1Removed(quint32 name); /** * Emitted whenever a zxdg_exporter_v2 interface gets removed. * @param name The name for the removed interface * @since 5.40 **/ void exporterUnstableV2Removed(quint32 name); /** * Emitted whenever a zxdg_importer_v2 interface gets removed. * @param name The name for the removed interface * @since 5.40 **/ void importerUnstableV2Removed(quint32 name); + + /** + * Emitted whenever a zwp_idle_inhibit_manager_v1 interface gets removed. + * @param name The name of the removed interface + * @since 5.41 + **/ + void idleInhibitManagerUnstableV1Removed(quint32 name); + + /** + * Emitted whenever a org_kde_kwin_appmenu_manager gets removed. + * @param name The name of the removed interface + * @since 5.42 + **/ + void appMenuRemoved(quint32 name); + + /** + * Emitted whenever a org_kde_kwin_server_decoration_palette_manager gets removed. + * @param name The name of the removed interface + * @since 5.42 + **/ + void serverSideDecorationPaletteManagerRemoved(quint32 name); + ///@} /** * Generic announced signal which gets emitted whenever an interface gets * announced. * * This signal is emitted before the dedicated signals are handled. If one * wants to know about one of the well-known interfaces use the dedicated * signals instead. Especially the bind methods might fail before the dedicated * signals are emitted. * * @param interface The interface (e.g. wl_compositor) which is announced * @param name The name for the announced interface * @param version The maximum supported version of the announced interface **/ void interfaceAnnounced(QByteArray interface, quint32 name, quint32 version); /** * Generic removal signal which gets emitted whenever an interface gets removed. * * This signal is emitted after the dedicated signals are handled. * * @param name The name for the removed interface **/ void interfaceRemoved(quint32 name); /** * Emitted when the Wayland display is done flushing the initial interface * callbacks, announcing wl_display properties. This can be used to compress * events. Note that this signal is emitted only after announcing interfaces, * such as outputs, but not after receiving callbacks of interface properties, * such as the output's geometry, modes, etc.. * This signal is emitted from the wl_display_sync callback. **/ void interfacesAnnounced(); Q_SIGNALS: /* * Emitted when the registry has been destroyed rather than released */ void registryDestroyed(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/server_decoration_palette.cpp b/src/client/server_decoration_palette.cpp new file mode 100644 index 0000000..d5207e1 --- /dev/null +++ b/src/client/server_decoration_palette.cpp @@ -0,0 +1,184 @@ +/**************************************************************************** +Copyright 2017 David Edmundson + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#include "server_decoration_palette.h" +#include "event_queue.h" +#include "surface.h" +#include "wayland_pointer_p.h" + +#include + +#include + +namespace KWayland +{ +namespace Client +{ + +class ServerSideDecorationPaletteManager::Private +{ +public: + Private() = default; + + void setup(org_kde_kwin_server_decoration_palette_manager *arg); + + WaylandPointer serverdecomanager; + EventQueue *queue = nullptr; +}; + +ServerSideDecorationPaletteManager::ServerSideDecorationPaletteManager(QObject *parent) + : QObject(parent) + , d(new Private) +{ +} + +void ServerSideDecorationPaletteManager::Private::setup(org_kde_kwin_server_decoration_palette_manager *arg) +{ + Q_ASSERT(arg); + Q_ASSERT(!serverdecomanager); + serverdecomanager.setup(arg); +} + +ServerSideDecorationPaletteManager::~ServerSideDecorationPaletteManager() +{ + release(); +} + +void ServerSideDecorationPaletteManager::setup(org_kde_kwin_server_decoration_palette_manager *serverdecomanager) +{ + d->setup(serverdecomanager); +} + +void ServerSideDecorationPaletteManager::release() +{ + d->serverdecomanager.release(); +} + +void ServerSideDecorationPaletteManager::destroy() +{ + d->serverdecomanager.destroy(); +} + +ServerSideDecorationPaletteManager::operator org_kde_kwin_server_decoration_palette_manager*() { + return d->serverdecomanager; +} + +ServerSideDecorationPaletteManager::operator org_kde_kwin_server_decoration_palette_manager*() const { + return d->serverdecomanager; +} + +bool ServerSideDecorationPaletteManager::isValid() const +{ + return d->serverdecomanager.isValid(); +} + +void ServerSideDecorationPaletteManager::setEventQueue(EventQueue *queue) +{ + d->queue = queue; +} + +EventQueue *ServerSideDecorationPaletteManager::eventQueue() +{ + return d->queue; +} + +ServerSideDecorationPalette *ServerSideDecorationPaletteManager::create(Surface *surface, QObject *parent) +{ + Q_ASSERT(isValid()); + auto p = new ServerSideDecorationPalette(parent); + auto w = org_kde_kwin_server_decoration_palette_manager_create(d->serverdecomanager, *surface); + if (d->queue) { + d->queue->addProxy(w); + } + p->setup(w); + + return p; +} + +class ServerSideDecorationPalette::Private +{ +public: + Private(ServerSideDecorationPalette *q); + + void setup(org_kde_kwin_server_decoration_palette *arg); + + WaylandPointer decoration_palette; + +private: + ServerSideDecorationPalette *q; +}; + +ServerSideDecorationPalette::Private::Private(ServerSideDecorationPalette *q) + : q(q) +{ +} + +ServerSideDecorationPalette::ServerSideDecorationPalette(QObject *parent) + : QObject(parent) + , d(new Private(this)) +{ +} + +void ServerSideDecorationPalette::Private::setup(org_kde_kwin_server_decoration_palette *arg) +{ + Q_ASSERT(arg); + Q_ASSERT(!decoration_palette); + decoration_palette.setup(arg); +} + +ServerSideDecorationPalette::~ServerSideDecorationPalette() +{ + release(); +} + +void ServerSideDecorationPalette::setup(org_kde_kwin_server_decoration_palette *decoration_palette) +{ + d->setup(decoration_palette); +} + +void ServerSideDecorationPalette::release() +{ + d->decoration_palette.release(); +} + +void ServerSideDecorationPalette::destroy() +{ + d->decoration_palette.destroy(); +} + +ServerSideDecorationPalette::operator org_kde_kwin_server_decoration_palette*() { + return d->decoration_palette; +} + +ServerSideDecorationPalette::operator org_kde_kwin_server_decoration_palette*() const { + return d->decoration_palette; +} + +bool ServerSideDecorationPalette::isValid() const +{ + return d->decoration_palette.isValid(); +} + +void ServerSideDecorationPalette::setPalette(const QString &palette) +{ + Q_ASSERT(isValid()); + org_kde_kwin_server_decoration_palette_set_palette(*this, palette.toUtf8()); +} +} +} diff --git a/src/client/server_decoration_palette.h b/src/client/server_decoration_palette.h new file mode 100644 index 0000000..85d0932 --- /dev/null +++ b/src/client/server_decoration_palette.h @@ -0,0 +1,198 @@ +/**************************************************************************** +Copyright 2017 David Edmundson + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#ifndef KWAYLAND_CLIENT_SERVER_DECORATION_PALETTE_H +#define KWAYLAND_CLIENT_SERVER_DECORATION_PALETTE_H + +#include + +#include + +struct org_kde_kwin_server_decoration_palette_manager; +struct org_kde_kwin_server_decoration_palette; + +namespace KWayland +{ +namespace Client +{ + +class EventQueue; +class Surface; +class ServerSideDecorationPalette; + +/** + * @short Wrapper for the org_kde_kwin_server_decoration_palette_manager interface. + * + * This class provides a convenient wrapper for the org_kde_kwin_server_decoration_palette_manager interface. + * + * To use this class one needs to interact with the Registry. There are two + * possible ways to create the ServerSideDecorationPaletteManager interface: + * @code + * ServerSideDecorationPaletteManager *c = registry->createServerSideDecorationPaletteManager(name, version); + * @endcode + * + * This creates the ServerSideDecorationPaletteManager and sets it up directly. As an alternative this + * can also be done in a more low level way: + * @code + * ServerSideDecorationPaletteManager *c = new ServerSideDecorationPaletteManager; + * c->setup(registry->bindServerSideDecorationPaletteManager(name, version)); + * @endcode + * + * The ServerSideDecorationPaletteManager can be used as a drop-in replacement for any org_kde_kwin_server_decoration_palette_manager + * pointer as it provides matching cast operators. + * + * @see Registry + **/ +class KWAYLANDCLIENT_EXPORT ServerSideDecorationPaletteManager : public QObject +{ + Q_OBJECT +public: + /** + * Creates a new ServerSideDecorationPaletteManager. + * Note: after constructing the ServerSideDecorationPaletteManager it is not yet valid and one needs + * to call setup. In order to get a ready to use ServerSideDecorationPaletteManager prefer using + * Registry::createServerSideDecorationPaletteManager. + **/ + explicit ServerSideDecorationPaletteManager(QObject *parent = nullptr); + virtual ~ServerSideDecorationPaletteManager(); + + /** + * Setup this ServerSideDecorationPaletteManager to manage the @p serverSideDecorationPaletteManager. + * When using Registry::createServerSideDecorationPaletteManager there is no need to call this + * method. + **/ + void setup(org_kde_kwin_server_decoration_palette_manager *serverSideDecorationPaletteManager); + /** + * @returns @c true if managing a org_kde_kwin_server_decoration_palette_manager. + **/ + bool isValid() const; + /** + * Releases the org_kde_kwin_server_decoration_palette_manager interface. + * After the interface has been released the ServerSideDecorationPaletteManager instance is no + * longer valid and can be setup with another org_kde_kwin_server_decoration_palette_manager interface. + **/ + void release(); + /** + * Destroys the data held by this ServerSideDecorationPaletteManager. + * This method is supposed to be used when the connection to the Wayland + * server goes away. If the connection is not valid anymore, it's not + * possible to call release anymore as that calls into the Wayland + * connection and the call would fail. This method cleans up the data, so + * that the instance can be deleted or set up to a new org_kde_kwin_server_decoration_palette_manager interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, palettemanager, &ServerSideDecorationPaletteManager::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + /** + * Sets the @p queue to use for creating objects with this ServerSideDecorationPaletteManager. + **/ + void setEventQueue(EventQueue *queue); + /** + * @returns The event queue to use for creating objects with this ServerSideDecorationPaletteManager. + **/ + EventQueue *eventQueue(); + + ServerSideDecorationPalette *create(Surface *surface, QObject *parent = nullptr); + + operator org_kde_kwin_server_decoration_palette_manager*(); + operator org_kde_kwin_server_decoration_palette_manager*() const; + +Q_SIGNALS: + /** + * The corresponding global for this interface on the Registry got removed. + * + * This signal gets only emitted if the ServerSideDecorationPaletteManager got created by + * Registry::createServerSideDecorationPaletteManager + **/ + void removed(); + +private: + class Private; + QScopedPointer d; +}; + +class KWAYLANDCLIENT_EXPORT ServerSideDecorationPalette : public QObject +{ + Q_OBJECT +public: + virtual ~ServerSideDecorationPalette(); + + /** + * Setup this ServerSideDecorationPalette to manage the @p serversidedecorationpalette. + * When using ServerSideDecorationPaletteManager::create there is no need to call this + * method. + **/ + void setup(org_kde_kwin_server_decoration_palette *serversidedecorationpalette); + /** + * @returns @c true if managing a org_kde_kwin_server_decoration_palette. + **/ + bool isValid() const; + /** + * Releases the org_kde_kwin_server_decoration_palette interface. + * After the interface has been released the ServerSideDecorationPalette instance is no + * longer valid and can be setup with another org_kde_kwin_server_decoration_palette interface. + **/ + void release(); + /** + * Destroys the data held by this ServerSideDecorationPalette. + * This method is supposed to be used when the connection to the Wayland + * server goes away. If the connection is not valid anymore, it's not + * possible to call release anymore as that calls into the Wayland + * connection and the call would fail. This method cleans up the data, so + * that the instance can be deleted or set up to a new org_kde_kwin_server_decoration_palette interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, palette, &ServerSideDecorationPalette::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + /** + * Sets the palette to be used by the server side decorations. + * Absolute file path, or name of palette in the user's config directory. + * If set to empty the default palette will be used. + */ + void setPalette(const QString &palette); + + operator org_kde_kwin_server_decoration_palette*(); + operator org_kde_kwin_server_decoration_palette*() const; + +private: + friend class ServerSideDecorationPaletteManager; + explicit ServerSideDecorationPalette(QObject *parent = nullptr); + class Private; + QScopedPointer d; +}; + + +} +} + +#endif diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index f2c9c5e..b8f0f51 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -1,247 +1,322 @@ set(SERVER_LIB_SRCS + appmenu_interface.cpp buffer_interface.cpp clientconnection.cpp compositor_interface.cpp datadevice_interface.cpp datadevicemanager_interface.cpp dataoffer_interface.cpp datasource_interface.cpp display.cpp dpms_interface.cpp + filtered_display.cpp global.cpp idle_interface.cpp + idleinhibit_interface.cpp + idleinhibit_interface_v1.cpp fakeinput_interface.cpp keyboard_interface.cpp remote_access_interface.cpp outputconfiguration_interface.cpp outputchangeset.cpp outputmanagement_interface.cpp outputdevice_interface.cpp logging.cpp output_interface.cpp pointer_interface.cpp plasmashell_interface.cpp plasmawindowmanagement_interface.cpp pointerconstraints_interface.cpp pointerconstraints_interface_v1.cpp pointergestures_interface.cpp pointergestures_interface_v1.cpp qtsurfaceextension_interface.cpp region_interface.cpp relativepointer_interface.cpp relativepointer_interface_v1.cpp resource.cpp seat_interface.cpp slide_interface.cpp shadow_interface.cpp blur_interface.cpp contrast_interface.cpp server_decoration_interface.cpp + server_decoration_palette_interface.cpp shell_interface.cpp surface_interface.cpp subcompositor_interface.cpp touch_interface.cpp textinput_interface.cpp textinput_interface_v0.cpp textinput_interface_v2.cpp xdgshell_interface.cpp xdgshell_v5_interface.cpp xdgforeign_v2_interface.cpp xdgforeign_interface.cpp xdgshell_v6_interface.cpp ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/output-management.xml BASENAME output-management ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/outputdevice.xml BASENAME org_kde_kwin_outputdevice ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/plasma-shell.xml BASENAME plasma-shell ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/plasma-window-management.xml BASENAME plasma-window-management ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/surface-extension.xml BASENAME qt-surface-extension ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/idle.xml BASENAME idle ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/fake-input.xml BASENAME fake-input ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/shadow.xml BASENAME shadow ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/dpms.xml BASENAME dpms ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/blur.xml BASENAME blur ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/contrast.xml BASENAME contrast ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/relative-pointer-unstable-v1.xml BASENAME relativepointer-unstable-v1 ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/slide.xml BASENAME slide ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/server-decoration.xml BASENAME server_decoration ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/text-input.xml BASENAME text ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/text-input-unstable-v2.xml BASENAME text-input-unstable-v2 ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/xdg-shell-unstable-v5.xml BASENAME xdg-shell-v5 ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/xdg-shell-unstable-v6.xml BASENAME xdg-shell-v6 ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/pointer-gestures-unstable-v1.xml BASENAME pointer-gestures-unstable-v1 ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/pointer-constraints-unstable-v1.xml BASENAME pointer-constraints-unstable-v1 ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/xdg-foreign-unstable-v2.xml BASENAME xdg-foreign-unstable-v2 ) +ecm_add_wayland_server_protocol(SERVER_LIB_SRCS + PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/idle-inhibit-unstable-v1.xml + BASENAME idle-inhibit-unstable-v1 +) + +ecm_add_wayland_server_protocol(SERVER_LIB_SRCS + PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/appmenu.xml + BASENAME appmenu +) + +ecm_add_wayland_server_protocol(SERVER_LIB_SRCS + PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/server-decoration-palette.xml + BASENAME server_decoration_palette +) + ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/remote-access.xml BASENAME remote-access ) +set(SERVER_GENERATED_SRCS + ${CMAKE_CURRENT_BINARY_DIR}/wayland-output-management-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-output-management-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-org_kde_kwin_outputdevice-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-org_kde_kwin_outputdevice-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-shell-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-shell-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-window-management-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-window-management-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-qt-surface-extension-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-qt-surface-extension-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-idle-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-idle-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-fake-input-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-fake-input-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-shadow-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-shadow-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-dpms-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-dpms-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-blur-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-blur-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-contrast-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-contrast-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-relativepointer-unstable-v1-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-relativepointer-unstable-v1-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-slide-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-slide-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-server_decoration-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-server_decoration-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-server_decoration_palette-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-server_decoration_palette-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-text-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-text-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-text-input-unstable-v2-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-text-input-unstable-v2-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-xdg-shell-v5-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-xdg-shell-v5-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-xdg-shell-v6-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-xdg-shell-v6-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-pointer-gestures-unstable-v1-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-pointer-gestures-unstable-v1-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-pointer-constraints-unstable-v1-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-pointer-constraints-unstable-v1-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-xdg-foreign-unstable-v2-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-xdg-foreign-unstable-v2-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-idle-inhibit-unstable-v1-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-idle-inhibit-unstable-v1-server-protocol.h +) + +set_source_files_properties(${SERVER_GENERATED_SRCS} PROPERTIES SKIP_AUTOMOC ON) + add_library(KF5WaylandServer ${SERVER_LIB_SRCS}) generate_export_header(KF5WaylandServer BASE_NAME KWaylandServer EXPORT_FILE_NAME KWayland/Server/kwaylandserver_export.h ) add_library(KF5::WaylandServer ALIAS KF5WaylandServer) target_include_directories(KF5WaylandServer INTERFACE "$") target_link_libraries(KF5WaylandServer PUBLIC Qt5::Gui PRIVATE Wayland::Server EGL::EGL Qt5::Concurrent ) set_target_properties(KF5WaylandServer PROPERTIES VERSION ${KWAYLAND_VERSION_STRING} SOVERSION ${KWAYLAND_SOVERSION} EXPORT_NAME WaylandServer ) install(TARGETS KF5WaylandServer EXPORT KF5WaylandTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) set(SERVER_LIB_HEADERS ${CMAKE_CURRENT_BINARY_DIR}/KWayland/Server/kwaylandserver_export.h + appmenu_interface.h blur_interface.h contrast_interface.h buffer_interface.h clientconnection.h compositor_interface.h datadevice_interface.h datadevicemanager_interface.h dataoffer_interface.h datasource_interface.h display.h dpms_interface.h + filtered_display.h fakeinput_interface.h global.h idle_interface.h + idleinhibit_interface.h keyboard_interface.h remote_access_interface.h outputdevice_interface.h outputchangeset.h outputconfiguration_interface.h outputmanagement_interface.h output_interface.h pointer_interface.h pointerconstraints_interface.h pointergestures_interface.h plasmashell_interface.h plasmawindowmanagement_interface.h qtsurfaceextension_interface.h region_interface.h relativepointer_interface.h resource.h seat_interface.h server_decoration_interface.h + server_decoration_palette_interface.h shadow_interface.h shell_interface.h slide_interface.h subcompositor_interface.h surface_interface.h textinput_interface.h touch_interface.h xdgshell_interface.h xdgforeign_interface.h ) install(FILES ${SERVER_LIB_HEADERS} DESTINATION ${KF5_INCLUDE_INSTALL_DIR}/KWayland/Server COMPONENT Devel ) # make available to ecm_add_qch in parent folder set(KWaylandServer_APIDOX_SRCS ${SERVER_LIB_HEADERS} PARENT_SCOPE) include(ECMGeneratePriFile) ecm_generate_pri_file(BASE_NAME KWaylandServer LIB_NAME KF5WaylandServer DEPS "core" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) diff --git a/src/server/appmenu_interface.cpp b/src/server/appmenu_interface.cpp new file mode 100644 index 0000000..3745229 --- /dev/null +++ b/src/server/appmenu_interface.cpp @@ -0,0 +1,216 @@ +/**************************************************************************** +Copyright 2017 David Edmundson + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#include "appmenu_interface.h" +#include "display.h" +#include "surface_interface.h" +#include "global_p.h" +#include "resource_p.h" +#include "logging_p.h" + +#include + +#include + +namespace KWayland +{ +namespace Server +{ +class AppMenuManagerInterface::Private : public Global::Private +{ +public: + Private(AppMenuManagerInterface *q, Display *d); + + QVector appmenus; +private: + void bind(wl_client *client, uint32_t version, uint32_t id) override; + + static void unbind(wl_resource *resource); + static Private *cast(wl_resource *r) { + return reinterpret_cast(wl_resource_get_user_data(r)); + } + + static void createCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface); + + AppMenuManagerInterface *q; + static const struct org_kde_kwin_appmenu_manager_interface s_interface; + static const quint32 s_version; +}; + +const quint32 AppMenuManagerInterface::Private::s_version = 1; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct org_kde_kwin_appmenu_manager_interface AppMenuManagerInterface::Private::s_interface = { + createCallback +}; +#endif + +void AppMenuManagerInterface::Private::createCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface) +{ + auto p = reinterpret_cast(wl_resource_get_user_data(resource)); + Q_ASSERT(p); + + SurfaceInterface *s = SurfaceInterface::get(surface); + if (!s) { + // TODO: send error? + qCWarning(KWAYLAND_SERVER) << "ServerSideDecorationInterface requested for non existing SurfaceInterface"; + return; + } + auto appmenu = new AppMenuInterface(p->q, s, resource); + appmenu->create(p->display->getConnection(client), wl_resource_get_version(resource), id); + if (!appmenu->resource()) { + wl_resource_post_no_memory(resource); + delete appmenu; + return; + } + p->appmenus.append(appmenu); + QObject::connect(appmenu, &QObject::destroyed, p->q, [=]() { + p->appmenus.removeOne(appmenu); + }); + emit p->q->appMenuCreated(appmenu); +} + +AppMenuManagerInterface::Private::Private(AppMenuManagerInterface *q, Display *d) + : Global::Private(d, &org_kde_kwin_appmenu_manager_interface, s_version) + , q(q) +{ +} + +void AppMenuManagerInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) +{ + auto c = display->getConnection(client); + wl_resource *resource = c->createResource(&org_kde_kwin_appmenu_manager_interface, qMin(version, s_version), id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &s_interface, this, unbind); +} + +void AppMenuManagerInterface::Private::unbind(wl_resource *resource) +{ + Q_UNUSED(resource) +} + +class AppMenuInterface::Private : public Resource::Private +{ +public: + Private(AppMenuInterface *q, AppMenuManagerInterface *c, SurfaceInterface *surface, wl_resource *parentResource); + ~Private(); + + + SurfaceInterface *surface; + InterfaceAddress address; +private: + static void setAddressCallback(wl_client *client, wl_resource *resource, const char * service_name, const char * object_path); + + AppMenuInterface *q_func() { + return reinterpret_cast(q); + } + static AppMenuInterface *get(SurfaceInterface *s); + static const struct org_kde_kwin_appmenu_interface s_interface; +}; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct org_kde_kwin_appmenu_interface AppMenuInterface::Private::s_interface = { + setAddressCallback, + resourceDestroyedCallback +}; +#endif + +void AppMenuInterface::Private::setAddressCallback(wl_client *client, wl_resource *resource, const char * service_name, const char * object_path) +{ + Q_UNUSED(client); + auto p = reinterpret_cast(wl_resource_get_user_data(resource)); + Q_ASSERT(p); + + if (p->address.serviceName == QLatin1String(service_name) && + p->address.objectPath == QLatin1String(object_path)) { + return; + } + p->address.serviceName = QString::fromLatin1(service_name); + p->address.objectPath = QString::fromLatin1(object_path); + emit p->q_func()->addressChanged(p->address); +} + +AppMenuInterface::Private::Private(AppMenuInterface *q, AppMenuManagerInterface *c, SurfaceInterface *s, wl_resource *parentResource) + : Resource::Private(q, c, parentResource, &org_kde_kwin_appmenu_interface, &s_interface), + surface(s) +{ +} + +AppMenuInterface::Private::~Private() +{ + if (resource) { + wl_resource_destroy(resource); + resource = nullptr; + } +} + +AppMenuManagerInterface::AppMenuManagerInterface(Display *display, QObject *parent) + : Global(new Private(this, display), parent) +{ +} + +AppMenuManagerInterface::~AppMenuManagerInterface() +{ +} + +AppMenuManagerInterface::Private *AppMenuManagerInterface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +AppMenuInterface* AppMenuManagerInterface::appMenuForSurface(SurfaceInterface *surface) +{ + Q_D(); + for (AppMenuInterface* menu: d->appmenus) { + if (menu->surface() == surface) { + return menu; + } + } + return nullptr; +} + +AppMenuInterface::AppMenuInterface(AppMenuManagerInterface *parent, SurfaceInterface *s, wl_resource *parentResource): + Resource(new Private(this, parent, s, parentResource)) +{ +} + +AppMenuInterface::Private *AppMenuInterface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +AppMenuInterface::~AppMenuInterface() +{} + +AppMenuInterface::InterfaceAddress AppMenuInterface::address() const { + Q_D(); + return d->address; +} + +SurfaceInterface* AppMenuInterface::surface() const { + Q_D(); + return d->surface; +} + +}//namespace +} + diff --git a/src/server/appmenu_interface.h b/src/server/appmenu_interface.h new file mode 100644 index 0000000..4213650 --- /dev/null +++ b/src/server/appmenu_interface.h @@ -0,0 +1,116 @@ +/**************************************************************************** +Copyright 2017 David Edmundson + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#ifndef KWAYLAND_SERVER_APPMENU_H +#define KWAYLAND_SERVER_APPMENU_H + +#include "global.h" +#include "resource.h" + +#include + +namespace KWayland +{ +namespace Server +{ + +class Display; +class SurfaceInterface; +class AppMenuInterface; + +/** + * Provides the DBus service name and object path to a AppMenu DBus interface. + * + * This global can be used for clients to bind AppmenuInterface instances + * and notifies when a new one is created + * @since 5.42 + */ +class KWAYLANDSERVER_EXPORT AppMenuManagerInterface : public Global +{ + Q_OBJECT +public: + virtual ~AppMenuManagerInterface(); + /** + * Returns any existing appMenu for a given surface + * This returns a null pointer if no AppMenuInterface exists. + */ + AppMenuInterface* appMenuForSurface(SurfaceInterface *); + +Q_SIGNALS: + /** + * Emitted whenever a new AppmenuInterface is created. + **/ + void appMenuCreated(KWayland::Server::AppMenuInterface*); + +private: + explicit AppMenuManagerInterface(Display *display, QObject *parent = nullptr); + friend class Display; + class Private; + Private *d_func() const; +}; + +/** + * Provides the DBus service name and object path to a AppMenu DBus interface. + * This interface is attached to a wl_surface and provides access to where + * the AppMenu DBus interface is registered. + * @since 5.42 + */ +class KWAYLANDSERVER_EXPORT AppMenuInterface : public Resource +{ + Q_OBJECT +public: + /** + * Structure containing DBus service name and path + */ + struct InterfaceAddress { + /** Service name of host with the AppMenu object*/ + QString serviceName; + /** Object path of the AppMenu interface*/ + QString objectPath; + }; + virtual ~AppMenuInterface(); + + /** + * @returns the service name and object path or empty strings if unset + */ + InterfaceAddress address() const; + + /** + * @returns The SurfaceInterface this AppmenuInterface references. + **/ + SurfaceInterface *surface() const; + +Q_SIGNALS: + /** + * Emitted when the address changes or is first received + */ + void addressChanged(KWayland::Server::AppMenuInterface::InterfaceAddress); + +private: + explicit AppMenuInterface(AppMenuManagerInterface *parent, SurfaceInterface *s, wl_resource *parentResource); + friend class AppMenuManagerInterface; + + class Private; + Private *d_func() const; +}; + +} +} + +#endif diff --git a/src/server/datadevice_interface.cpp b/src/server/datadevice_interface.cpp index efcbdff..8ba19ba 100644 --- a/src/server/datadevice_interface.cpp +++ b/src/server/datadevice_interface.cpp @@ -1,303 +1,350 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "datadevice_interface.h" #include "datadevicemanager_interface.h" -#include "dataoffer_interface.h" +#include "dataoffer_interface_p.h" #include "datasource_interface.h" #include "display.h" #include "resource_p.h" #include "pointer_interface.h" #include "seat_interface.h" #include "surface_interface.h" // Wayland #include namespace KWayland { namespace Server { class DataDeviceInterface::Private : public Resource::Private { public: Private(SeatInterface *seat, DataDeviceInterface *q, DataDeviceManagerInterface *manager, wl_resource *parentResource); ~Private(); DataOfferInterface *createDataOffer(DataSourceInterface *source); SeatInterface *seat; DataSourceInterface *source = nullptr; SurfaceInterface *surface = nullptr; SurfaceInterface *icon = nullptr; DataSourceInterface *selection = nullptr; QMetaObject::Connection selectionUnboundConnection; QMetaObject::Connection selectionDestroyedConnection; struct Drag { SurfaceInterface *surface = nullptr; QMetaObject::Connection destroyConnection; QMetaObject::Connection pointerPosConnection; + QMetaObject::Connection sourceActionConnection; + QMetaObject::Connection targetActionConnection; quint32 serial = 0; }; Drag drag; private: DataDeviceInterface *q_func() { return reinterpret_cast(q); } void startDrag(DataSourceInterface *dataSource, SurfaceInterface *origin, SurfaceInterface *icon, quint32 serial); void setSelection(DataSourceInterface *dataSource); static void startDragCallback(wl_client *client, wl_resource *resource, wl_resource *source, wl_resource *origin, wl_resource *icon, uint32_t serial); static void setSelectionCallback(wl_client *client, wl_resource *resource, wl_resource *source, uint32_t serial); static const struct wl_data_device_interface s_interface; }; #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct wl_data_device_interface DataDeviceInterface::Private::s_interface = { startDragCallback, setSelectionCallback, resourceDestroyedCallback }; #endif DataDeviceInterface::Private::Private(SeatInterface *seat, DataDeviceInterface *q, DataDeviceManagerInterface *manager, wl_resource *parentResource) : Resource::Private(q, manager, parentResource, &wl_data_device_interface, &s_interface) , seat(seat) { } DataDeviceInterface::Private::~Private() = default; void DataDeviceInterface::Private::startDragCallback(wl_client *client, wl_resource *resource, wl_resource *source, wl_resource *origin, wl_resource *icon, uint32_t serial) { Q_UNUSED(client) Q_UNUSED(serial) // TODO: verify serial cast(resource)->startDrag(DataSourceInterface::get(source), SurfaceInterface::get(origin), SurfaceInterface::get(icon), serial); } void DataDeviceInterface::Private::startDrag(DataSourceInterface *dataSource, SurfaceInterface *origin, SurfaceInterface *i, quint32 serial) { // TODO: allow touch if (seat->hasImplicitPointerGrab(serial) && seat->focusedPointerSurface() != origin) { wl_resource_post_error(resource, 0, "Surface doesn't have pointer grab"); return; } // TODO: source is allowed to be null, handled client internally! source = dataSource; surface = origin; icon = i; drag.serial = serial; Q_Q(DataDeviceInterface); emit q->dragStarted(); } void DataDeviceInterface::Private::setSelectionCallback(wl_client *client, wl_resource *resource, wl_resource *source, uint32_t serial) { Q_UNUSED(client) Q_UNUSED(serial) // TODO: verify serial cast(resource)->setSelection(DataSourceInterface::get(source)); } void DataDeviceInterface::Private::setSelection(DataSourceInterface *dataSource) { + if (dataSource && dataSource->supportedDragAndDropActions() && wl_resource_get_version(dataSource->resource()) >= WL_DATA_SOURCE_ACTION_SINCE_VERSION) { + wl_resource_post_error(dataSource->resource(), WL_DATA_SOURCE_ERROR_INVALID_SOURCE, "Data source is for drag and drop"); + return; + } Q_Q(DataDeviceInterface); QObject::disconnect(selectionUnboundConnection); QObject::disconnect(selectionDestroyedConnection); if (selection) { selection->cancel(); } selection = dataSource; if (selection) { auto clearSelection = [this] { setSelection(nullptr); }; selectionUnboundConnection = QObject::connect(selection, &Resource::unbound, q, clearSelection); selectionDestroyedConnection = QObject::connect(selection, &QObject::destroyed, q, clearSelection); emit q->selectionChanged(selection); } else { selectionUnboundConnection = QMetaObject::Connection(); selectionDestroyedConnection = QMetaObject::Connection(); emit q->selectionCleared(); } } DataOfferInterface *DataDeviceInterface::Private::createDataOffer(DataSourceInterface *source) { if (!resource) { return nullptr; } Q_Q(DataDeviceInterface); DataOfferInterface *offer = new DataOfferInterface(source, q, resource); auto c = q->global()->display()->getConnection(wl_resource_get_client(resource)); offer->create(c, wl_resource_get_version(resource), 0); if (!offer->resource()) { // TODO: send error? delete offer; return nullptr; } wl_data_device_send_data_offer(resource, offer->resource()); offer->sendAllOffers(); return offer; } DataDeviceInterface::DataDeviceInterface(SeatInterface *seat, DataDeviceManagerInterface *parent, wl_resource *parentResource) : Resource(new Private(seat, this, parent, parentResource)) { } DataDeviceInterface::~DataDeviceInterface() = default; SeatInterface *DataDeviceInterface::seat() const { Q_D(); return d->seat; } DataSourceInterface *DataDeviceInterface::dragSource() const { Q_D(); return d->source; } SurfaceInterface *DataDeviceInterface::icon() const { Q_D(); return d->icon; } SurfaceInterface *DataDeviceInterface::origin() const { Q_D(); return d->surface; } DataSourceInterface *DataDeviceInterface::selection() const { Q_D(); return d->selection; } void DataDeviceInterface::sendSelection(DataDeviceInterface *other) { Q_D(); auto otherSelection = other->selection(); if (!otherSelection) { sendClearSelection(); return; } auto r = d->createDataOffer(otherSelection); if (!r) { return; } if (!d->resource) { return; } wl_data_device_send_selection(d->resource, r->resource()); } void DataDeviceInterface::sendClearSelection() { Q_D(); if (!d->resource) { return; } wl_data_device_send_selection(d->resource, nullptr); } void DataDeviceInterface::drop() { Q_D(); if (!d->resource) { return; } wl_data_device_send_drop(d->resource); + if (d->drag.pointerPosConnection) { + disconnect(d->drag.pointerPosConnection); + d->drag.pointerPosConnection = QMetaObject::Connection(); + } + disconnect(d->drag.destroyConnection); + d->drag.destroyConnection = QMetaObject::Connection(); + d->drag.surface = nullptr; client()->flush(); } void DataDeviceInterface::updateDragTarget(SurfaceInterface *surface, quint32 serial) { Q_D(); if (d->drag.surface) { if (d->resource && d->drag.surface->resource()) { wl_data_device_send_leave(d->resource); } if (d->drag.pointerPosConnection) { disconnect(d->drag.pointerPosConnection); d->drag.pointerPosConnection = QMetaObject::Connection(); } disconnect(d->drag.destroyConnection); d->drag.destroyConnection = QMetaObject::Connection(); d->drag.surface = nullptr; + if (d->drag.sourceActionConnection) { + disconnect(d->drag.sourceActionConnection); + d->drag.sourceActionConnection = QMetaObject::Connection(); + } + if (d->drag.targetActionConnection) { + disconnect(d->drag.targetActionConnection); + d->drag.targetActionConnection = QMetaObject::Connection(); + } // don't update serial, we need it } if (!surface) { + d->seat->dragSource()->dragSource()->dndAction(DataDeviceManagerInterface::DnDAction::None); return; } - DataOfferInterface *offer = d->createDataOffer(d->seat->dragSource()->dragSource()); + auto *source = d->seat->dragSource()->dragSource(); + DataOfferInterface *offer = d->createDataOffer(source); d->drag.surface = surface; if (d->seat->isDragPointer()) { d->drag.pointerPosConnection = connect(d->seat, &SeatInterface::pointerPosChanged, this, [this] { Q_D(); const QPointF pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos()); wl_data_device_send_motion(d->resource, d->seat->timestamp(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y())); client()->flush(); } ); } // TODO: same for touch d->drag.destroyConnection = connect(d->drag.surface, &QObject::destroyed, this, [this] { Q_D(); if (d->resource) { wl_data_device_send_leave(d->resource); } if (d->drag.pointerPosConnection) { disconnect(d->drag.pointerPosConnection); } d->drag = Private::Drag(); } ); // TODO: handle touch position const QPointF pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos()); wl_data_device_send_enter(d->resource, serial, surface->resource(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()), offer ? offer->resource() : nullptr); + if (offer) { + offer->d_func()->sendSourceActions(); + auto matchOffers = [source, offer] { + DataDeviceManagerInterface::DnDAction action{DataDeviceManagerInterface::DnDAction::None}; + if (source->supportedDragAndDropActions().testFlag(offer->preferredDragAndDropAction())) { + action = offer->preferredDragAndDropAction(); + } else { + if (source->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Copy) && + offer->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Copy)) { + action = DataDeviceManagerInterface::DnDAction::Copy; + } else if (source->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Move) && + offer->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Move)) { + action = DataDeviceManagerInterface::DnDAction::Move; + } else if (source->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Ask) && + offer->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Ask)) { + action = DataDeviceManagerInterface::DnDAction::Ask; + } + } + offer->dndAction(action); + source->dndAction(action); + }; + d->drag.targetActionConnection = connect(offer, &DataOfferInterface::dragAndDropActionsChanged, offer, matchOffers); + d->drag.sourceActionConnection = connect(source, &DataSourceInterface::supportedDragAndDropActionsChanged, source, matchOffers); + } d->client->flush(); } quint32 DataDeviceInterface::dragImplicitGrabSerial() const { Q_D(); return d->drag.serial; } DataDeviceInterface::Private *DataDeviceInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/datadevicemanager_interface.cpp b/src/server/datadevicemanager_interface.cpp index 168f6af..952d215 100644 --- a/src/server/datadevicemanager_interface.cpp +++ b/src/server/datadevicemanager_interface.cpp @@ -1,134 +1,135 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "datadevicemanager_interface.h" +#include "datasource_interface.h" #include "global_p.h" #include "display.h" #include "seat_interface_p.h" // Wayland #include namespace KWayland { namespace Server { class DataDeviceManagerInterface::Private : public Global::Private { public: Private(DataDeviceManagerInterface *q, Display *d); private: void bind(wl_client *client, uint32_t version, uint32_t id) override; void createDataSource(wl_client *client, wl_resource *resource, uint32_t id); void getDataDevice(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *seat); static void unbind(wl_resource *resource); static void createDataSourceCallback(wl_client *client, wl_resource *resource, uint32_t id); static void getDataDeviceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *seat); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } DataDeviceManagerInterface *q; static const struct wl_data_device_manager_interface s_interface; static const quint32 s_version; static const qint32 s_dataDeviceVersion; static const qint32 s_dataSourceVersion; }; -const quint32 DataDeviceManagerInterface::Private::s_version = 2; -const qint32 DataDeviceManagerInterface::Private::s_dataDeviceVersion = 2; -const qint32 DataDeviceManagerInterface::Private::s_dataSourceVersion = 1; +const quint32 DataDeviceManagerInterface::Private::s_version = 3; +const qint32 DataDeviceManagerInterface::Private::s_dataDeviceVersion = 3; +const qint32 DataDeviceManagerInterface::Private::s_dataSourceVersion = 3; #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct wl_data_device_manager_interface DataDeviceManagerInterface::Private::s_interface = { createDataSourceCallback, getDataDeviceCallback }; #endif DataDeviceManagerInterface::Private::Private(DataDeviceManagerInterface *q, Display *d) : Global::Private(d, &wl_data_device_manager_interface, s_version) , q(q) { } void DataDeviceManagerInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&wl_data_device_manager_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); // TODO: should we track? } void DataDeviceManagerInterface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) } void DataDeviceManagerInterface::Private::createDataSourceCallback(wl_client *client, wl_resource *resource, uint32_t id) { cast(resource)->createDataSource(client, resource, id); } void DataDeviceManagerInterface::Private::createDataSource(wl_client *client, wl_resource *resource, uint32_t id) { DataSourceInterface *dataSource = new DataSourceInterface(q, resource); dataSource->create(display->getConnection(client), qMin(wl_resource_get_version(resource), s_dataSourceVersion) , id); if (!dataSource->resource()) { wl_resource_post_no_memory(resource); delete dataSource; return; } emit q->dataSourceCreated(dataSource); } void DataDeviceManagerInterface::Private::getDataDeviceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *seat) { cast(resource)->getDataDevice(client, resource, id, seat); } void DataDeviceManagerInterface::Private::getDataDevice(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *seat) { SeatInterface *s = SeatInterface::get(seat); Q_ASSERT(s); DataDeviceInterface *dataDevice = new DataDeviceInterface(s, q, resource); dataDevice->create(display->getConnection(client), qMin(wl_resource_get_version(resource), s_dataDeviceVersion), id); if (!dataDevice->resource()) { wl_resource_post_no_memory(resource); return; } s->d_func()->registerDataDevice(dataDevice); emit q->dataDeviceCreated(dataDevice); } DataDeviceManagerInterface::DataDeviceManagerInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } DataDeviceManagerInterface::~DataDeviceManagerInterface() = default; } } diff --git a/src/server/datadevicemanager_interface.h b/src/server/datadevicemanager_interface.h index 25e07d0..bc8d3c5 100644 --- a/src/server/datadevicemanager_interface.h +++ b/src/server/datadevicemanager_interface.h @@ -1,60 +1,74 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #ifndef WAYLAND_SERVER_DATA_DEVICE_MANAGER_INTERFACE_H #define WAYLAND_SERVER_DATA_DEVICE_MANAGER_INTERFACE_H #include #include #include "global.h" #include "datadevice_interface.h" -#include "datasource_interface.h" namespace KWayland { namespace Server { class Display; +class DataSourceInterface; /** * @brief Represents the Global for wl_data_device_manager interface. * **/ class KWAYLANDSERVER_EXPORT DataDeviceManagerInterface : public Global { Q_OBJECT public: virtual ~DataDeviceManagerInterface(); + /** + * Drag and Drop actions supported by the DataSourceInterface. + * @since 5.XX + **/ + enum class DnDAction { + None = 0, + Copy = 1 << 0, + Move = 1 << 1, + Ask = 1 << 2 + }; + Q_DECLARE_FLAGS(DnDActions, DnDAction) + Q_SIGNALS: void dataSourceCreated(KWayland::Server::DataSourceInterface*); void dataDeviceCreated(KWayland::Server::DataDeviceInterface*); private: explicit DataDeviceManagerInterface(Display *display, QObject *parent = nullptr); friend class Display; class Private; }; } } +Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Server::DataDeviceManagerInterface::DnDActions) + #endif diff --git a/src/server/dataoffer_interface.cpp b/src/server/dataoffer_interface.cpp index 0b04653..b15ede8 100644 --- a/src/server/dataoffer_interface.cpp +++ b/src/server/dataoffer_interface.cpp @@ -1,130 +1,216 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ -#include "dataoffer_interface.h" +#include "dataoffer_interface_p.h" #include "datadevice_interface.h" #include "datasource_interface.h" -#include "resource_p.h" // Qt #include // Wayland #include namespace KWayland { namespace Server { -class DataOfferInterface::Private : public Resource::Private -{ -public: - Private(DataSourceInterface *source, DataDeviceInterface *parentInterface, DataOfferInterface *q, wl_resource *parentResource); - ~Private(); - DataSourceInterface *source; - DataDeviceInterface *dataDevice; - -private: - DataOfferInterface *q_func() { - return reinterpret_cast(q); - } - void receive(const QString &mimeType, qint32 fd); - static void acceptCallback(wl_client *client, wl_resource *resource, uint32_t serial, const char *mimeType); - static void receiveCallback(wl_client *client, wl_resource *resource, const char *mimeType, int32_t fd); - - static const struct wl_data_offer_interface s_interface; -}; - #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct wl_data_offer_interface DataOfferInterface::Private::s_interface = { acceptCallback, receiveCallback, - resourceDestroyedCallback + resourceDestroyedCallback, + finishCallback, + setActionsCallback }; #endif DataOfferInterface::Private::Private(DataSourceInterface *source, DataDeviceInterface *parentInterface, DataOfferInterface *q, wl_resource *parentResource) : Resource::Private(q, nullptr, parentResource, &wl_data_offer_interface, &s_interface) , source(source) , dataDevice(parentInterface) { // TODO: connect to new selections } DataOfferInterface::Private::~Private() = default; void DataOfferInterface::Private::acceptCallback(wl_client *client, wl_resource *resource, uint32_t serial, const char *mimeType) { Q_UNUSED(client) Q_UNUSED(serial) auto p = cast(resource); if (!p->source) { return; } p->source->accept(mimeType ? QString::fromUtf8(mimeType) : QString()); } void DataOfferInterface::Private::receiveCallback(wl_client *client, wl_resource *resource, const char *mimeType, int32_t fd) { Q_UNUSED(client) cast(resource)->receive(QString::fromUtf8(mimeType), fd); } void DataOfferInterface::Private::receive(const QString &mimeType, qint32 fd) { source->requestData(mimeType, fd); } +void DataOfferInterface::Private::finishCallback(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) + auto p = cast(resource); + if (!p->source) { + return; + } + p->source->dndFinished(); + // TODO: It is a client error to perform other requests than wl_data_offer.destroy after this one +} + +void DataOfferInterface::Private::setActionsCallback(wl_client *client, wl_resource *resource, uint32_t dnd_actions, uint32_t preferred_action) +{ + // TODO: check it's drag and drop, otherwise send error + Q_UNUSED(client) + DataDeviceManagerInterface::DnDActions supportedActions; + if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) { + supportedActions |= DataDeviceManagerInterface::DnDAction::Copy; + } + if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) { + supportedActions |= DataDeviceManagerInterface::DnDAction::Move; + } + if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) { + supportedActions |= DataDeviceManagerInterface::DnDAction::Ask; + } + // verify that the no other actions are sent + if (dnd_actions & ~(WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)) { + wl_resource_post_error(resource, WL_DATA_OFFER_ERROR_INVALID_ACTION_MASK, "Invalid action mask"); + return; + } + if (preferred_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY && + preferred_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE && + preferred_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK && + preferred_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE) { + wl_resource_post_error(resource, WL_DATA_OFFER_ERROR_INVALID_ACTION, "Invalid preferred action"); + return; + } + + DataDeviceManagerInterface::DnDAction preferredAction = DataDeviceManagerInterface::DnDAction::None; + if (preferred_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) { + preferredAction = DataDeviceManagerInterface::DnDAction::Copy; + } else if (preferred_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) { + preferredAction = DataDeviceManagerInterface::DnDAction::Move; + } else if (preferred_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) { + preferredAction = DataDeviceManagerInterface::DnDAction::Ask; + } + + auto p = cast(resource); + p->supportedDnDActions = supportedActions; + p->preferredDnDAction = preferredAction; + emit p->q_func()->dragAndDropActionsChanged(); +} + +void DataOfferInterface::Private::sendSourceActions() +{ + if (!source) { + return; + } + if (wl_resource_get_version(resource) < WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) { + return; + } + uint32_t wlActions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + const auto actions = source->supportedDragAndDropActions(); + if (actions.testFlag(DataDeviceManagerInterface::DnDAction::Copy)) { + wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + } + if (actions.testFlag(DataDeviceManagerInterface::DnDAction::Move)) { + wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + } + if (actions.testFlag(DataDeviceManagerInterface::DnDAction::Ask)) { + wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; + } + wl_data_offer_send_source_actions(resource, wlActions); +} + DataOfferInterface::DataOfferInterface(DataSourceInterface *source, DataDeviceInterface *parentInterface, wl_resource *parentResource) : Resource(new Private(source, parentInterface, this, parentResource)) { Q_ASSERT(source); connect(source, &DataSourceInterface::mimeTypeOffered, this, [this](const QString &mimeType) { Q_D(); if (!d->resource) { return; } wl_data_offer_send_offer(d->resource, mimeType.toUtf8().constData()); } ); QObject::connect(source, &QObject::destroyed, this, [this] { Q_D(); d->source = nullptr; } ); } DataOfferInterface::~DataOfferInterface() = default; void DataOfferInterface::sendAllOffers() { Q_D(); for (const QString &mimeType : d->source->mimeTypes()) { wl_data_offer_send_offer(d->resource, mimeType.toUtf8().constData()); } } DataOfferInterface::Private *DataOfferInterface::d_func() const { return reinterpret_cast(d.data()); } +DataDeviceManagerInterface::DnDActions DataOfferInterface::supportedDragAndDropActions() const +{ + Q_D(); + return d->supportedDnDActions; +} + +DataDeviceManagerInterface::DnDAction DataOfferInterface::preferredDragAndDropAction() const +{ + Q_D(); + return d->preferredDnDAction; +} + +void DataOfferInterface::dndAction(DataDeviceManagerInterface::DnDAction action) +{ + Q_D(); + if (wl_resource_get_version(d->resource) < WL_DATA_OFFER_ACTION_SINCE_VERSION) { + return; + } + uint32_t wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + if (action == DataDeviceManagerInterface::DnDAction::Copy) { + wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + } else if (action == DataDeviceManagerInterface::DnDAction::Move ) { + wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + } else if (action == DataDeviceManagerInterface::DnDAction::Ask) { + wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; + } + wl_data_offer_send_action(d->resource, wlAction); +} + } } diff --git a/src/server/dataoffer_interface.h b/src/server/dataoffer_interface.h index 2412fbd..8e415d9 100644 --- a/src/server/dataoffer_interface.h +++ b/src/server/dataoffer_interface.h @@ -1,62 +1,89 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #ifndef WAYLAND_SERVER_DATA_OFFER_INTERFACE_H #define WAYLAND_SERVER_DATA_OFFER_INTERFACE_H #include #include #include "resource.h" +#include "datadevicemanager_interface.h" namespace KWayland { namespace Server { class DataDeviceInterface; class DataSourceInterface; /** * @brief Represents the Resource for the wl_data_offer interface. * **/ class KWAYLANDSERVER_EXPORT DataOfferInterface : public Resource { Q_OBJECT public: virtual ~DataOfferInterface(); void sendAllOffers(); + /** + * @returns The Drag and Drop actions supported by this DataOfferInterface. + * @since 5.42 + **/ + DataDeviceManagerInterface::DnDActions supportedDragAndDropActions() const; + + /** + * @returns The preferred Drag and Drop action of this DataOfferInterface. + * @since 5.42 + **/ + DataDeviceManagerInterface::DnDAction preferredDragAndDropAction() const; + + /** + * This event indicates the @p action selected by the compositor after matching the + * source/destination side actions. Only one action (or none) will be offered here. + * @since 5.42 + **/ + void dndAction(DataDeviceManagerInterface::DnDAction action); + +Q_SIGNALS: + /** + * Emitted whenever the supported or preferred Drag and Drop actions changed. + * @since 5.42 + **/ + void dragAndDropActionsChanged(); + private: friend class DataDeviceInterface; explicit DataOfferInterface(DataSourceInterface *source, DataDeviceInterface *parentInterface, wl_resource *parentResource); class Private; Private *d_func() const; }; } } Q_DECLARE_METATYPE(KWayland::Server::DataOfferInterface*) #endif diff --git a/src/server/dataoffer_interface_p.h b/src/server/dataoffer_interface_p.h new file mode 100644 index 0000000..e66c315 --- /dev/null +++ b/src/server/dataoffer_interface_p.h @@ -0,0 +1,61 @@ +/******************************************************************** +Copyright 2017 Martin Flöser + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +*********************************************************************/ +#ifndef KWAYLAND_SERVER_DATAOFFERINTERFACE_P_H +#define KWAYLAND_SERVER_DATAOFFERINTERFACE_P_H +#include "dataoffer_interface.h" +#include "datasource_interface.h" +#include "resource_p.h" +#include + +namespace KWayland +{ +namespace Server +{ + +class Q_DECL_HIDDEN DataOfferInterface::Private : public Resource::Private +{ +public: + Private(DataSourceInterface *source, DataDeviceInterface *parentInterface, DataOfferInterface *q, wl_resource *parentResource); + ~Private(); + DataSourceInterface *source; + DataDeviceInterface *dataDevice; + // defaults are set to sensible values for < version 3 interfaces + DataDeviceManagerInterface::DnDActions supportedDnDActions = DataDeviceManagerInterface::DnDAction::Copy | DataDeviceManagerInterface::DnDAction::Move; + DataDeviceManagerInterface::DnDAction preferredDnDAction = DataDeviceManagerInterface::DnDAction::Copy; + + void sendSourceActions(); + +private: + DataOfferInterface *q_func() { + return reinterpret_cast(q); + } + void receive(const QString &mimeType, qint32 fd); + static void acceptCallback(wl_client *client, wl_resource *resource, uint32_t serial, const char *mimeType); + static void receiveCallback(wl_client *client, wl_resource *resource, const char *mimeType, int32_t fd); + static void finishCallback(wl_client *client, wl_resource *resource); + static void setActionsCallback(wl_client *client, wl_resource *resource, uint32_t dnd_actions, uint32_t preferred_action); + + static const struct wl_data_offer_interface s_interface; +}; + +} +} + +#endif diff --git a/src/server/datasource_interface.cpp b/src/server/datasource_interface.cpp index ee2db95..8ebb240 100644 --- a/src/server/datasource_interface.cpp +++ b/src/server/datasource_interface.cpp @@ -1,133 +1,206 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "datasource_interface.h" #include "datadevicemanager_interface.h" #include "clientconnection.h" #include "resource_p.h" // Qt #include // Wayland #include // system #include namespace KWayland { namespace Server { class DataSourceInterface::Private : public Resource::Private { public: Private(DataSourceInterface *q, DataDeviceManagerInterface *parent, wl_resource *parentResource); ~Private(); QStringList mimeTypes; + DataDeviceManagerInterface::DnDActions supportedDnDActions = DataDeviceManagerInterface::DnDAction::None; private: DataSourceInterface *q_func() { return reinterpret_cast(q); } void offer(const QString &mimeType); static void offerCallback(wl_client *client, wl_resource *resource, const char *mimeType); + static void setActionsCallback(wl_client *client, wl_resource *resource, uint32_t dnd_actions); const static struct wl_data_source_interface s_interface; }; #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct wl_data_source_interface DataSourceInterface::Private::s_interface = { offerCallback, - resourceDestroyedCallback + resourceDestroyedCallback, + setActionsCallback }; #endif DataSourceInterface::Private::Private(DataSourceInterface *q, DataDeviceManagerInterface *parent, wl_resource *parentResource) : Resource::Private(q, parent, parentResource, &wl_data_source_interface, &s_interface) { } DataSourceInterface::Private::~Private() = default; void DataSourceInterface::Private::offerCallback(wl_client *client, wl_resource *resource, const char *mimeType) { Q_UNUSED(client) cast(resource)->offer(QString::fromUtf8(mimeType)); } void DataSourceInterface::Private::offer(const QString &mimeType) { mimeTypes << mimeType; Q_Q(DataSourceInterface); emit q->mimeTypeOffered(mimeType); } +void DataSourceInterface::Private::setActionsCallback(wl_client *client, wl_resource *resource, uint32_t dnd_actions) +{ + Q_UNUSED(client) + DataDeviceManagerInterface::DnDActions supportedActions; + if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) { + supportedActions |= DataDeviceManagerInterface::DnDAction::Copy; + } + if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) { + supportedActions |= DataDeviceManagerInterface::DnDAction::Move; + } + if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) { + supportedActions |= DataDeviceManagerInterface::DnDAction::Ask; + } + // verify that the no other actions are sent + if (dnd_actions & ~(WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)) { + wl_resource_post_error(resource, WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK, "Invalid action mask"); + return; + } + auto p = cast(resource); + if (p->supportedDnDActions!= supportedActions) { + p->supportedDnDActions = supportedActions; + emit p->q_func()->supportedDragAndDropActionsChanged(); + } +} + DataSourceInterface::DataSourceInterface(DataDeviceManagerInterface *parent, wl_resource *parentResource) : Resource(new Private(this, parent, parentResource)) { + if (wl_resource_get_version(parentResource) < WL_DATA_SOURCE_ACTION_SINCE_VERSION) { + Q_D(); + d->supportedDnDActions = DataDeviceManagerInterface::DnDAction::Copy; + } } DataSourceInterface::~DataSourceInterface() = default; void DataSourceInterface::accept(const QString &mimeType) { Q_D(); // TODO: does this require a sanity check on the possible mimeType? wl_data_source_send_target(d->resource, mimeType.isEmpty() ? nullptr : mimeType.toUtf8().constData()); } void DataSourceInterface::requestData(const QString &mimeType, qint32 fd) { Q_D(); // TODO: does this require a sanity check on the possible mimeType? if (d->resource) { wl_data_source_send_send(d->resource, mimeType.toUtf8().constData(), int32_t(fd)); } close(fd); } void DataSourceInterface::cancel() { Q_D(); if (!d->resource) { return; } wl_data_source_send_cancelled(d->resource); client()->flush(); } QStringList DataSourceInterface::mimeTypes() const { Q_D(); return d->mimeTypes; } DataSourceInterface *DataSourceInterface::get(wl_resource *native) { return Private::get(native); } DataSourceInterface::Private *DataSourceInterface::d_func() const { return reinterpret_cast(d.data()); } +DataDeviceManagerInterface::DnDActions DataSourceInterface::supportedDragAndDropActions() const +{ + Q_D(); + return d->supportedDnDActions; +} + +void DataSourceInterface::dropPerformed() +{ + Q_D(); + if (wl_resource_get_version(d->resource) < WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION) { + return; + } + wl_data_source_send_dnd_drop_performed(d->resource); +} + +void DataSourceInterface::dndFinished() +{ + Q_D(); + if (wl_resource_get_version(d->resource) < WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) { + return; + } + wl_data_source_send_dnd_finished(d->resource); +} + +void DataSourceInterface::dndAction(DataDeviceManagerInterface::DnDAction action) +{ + Q_D(); + if (wl_resource_get_version(d->resource) < WL_DATA_SOURCE_ACTION_SINCE_VERSION) { + return; + } + uint32_t wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + if (action == DataDeviceManagerInterface::DnDAction::Copy) { + wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + } else if (action == DataDeviceManagerInterface::DnDAction::Move ) { + wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + } else if (action == DataDeviceManagerInterface::DnDAction::Ask) { + wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; + } + wl_data_source_send_action(d->resource, wlAction); +} + } } diff --git a/src/server/datasource_interface.h b/src/server/datasource_interface.h index 4ed5a04..48c1340 100644 --- a/src/server/datasource_interface.h +++ b/src/server/datasource_interface.h @@ -1,68 +1,96 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #ifndef WAYLAND_SERVER_DATA_SOURCE_INTERFACE_H #define WAYLAND_SERVER_DATA_SOURCE_INTERFACE_H #include #include #include "resource.h" +#include "datadevicemanager_interface.h" namespace KWayland { namespace Server { -class DataDeviceManagerInterface; /** * @brief Represents the Resource for the wl_data_source interface. **/ class KWAYLANDSERVER_EXPORT DataSourceInterface : public Resource { Q_OBJECT public: virtual ~DataSourceInterface(); void accept(const QString &mimeType); void requestData(const QString &mimeType, qint32 fd); void cancel(); QStringList mimeTypes() const; static DataSourceInterface *get(wl_resource *native); + /** + * @returns The Drag and Drop actions supported by this DataSourceInterface. + * @since 5.42 + **/ + DataDeviceManagerInterface::DnDActions supportedDragAndDropActions() const; + + /** + * The user performed the drop action during a drag and drop operation. + * @since 5.42 + **/ + void dropPerformed(); + /** + * The drop destination finished interoperating with this data source. + * @since 5.42 + **/ + void dndFinished(); + /** + * This event indicates the @p action selected by the compositor after matching the + * source/destination side actions. Only one action (or none) will be offered here. + * @since 5.42 + **/ + void dndAction(DataDeviceManagerInterface::DnDAction action); + Q_SIGNALS: void mimeTypeOffered(const QString&); + /** + * Emitted whenever this DataSourceInterface changes the supported drag and drop actions + * @since 5.42 + **/ + void supportedDragAndDropActionsChanged(); private: friend class DataDeviceManagerInterface; explicit DataSourceInterface(DataDeviceManagerInterface *parent, wl_resource *parentResource); class Private; Private *d_func() const; }; } } Q_DECLARE_METATYPE(KWayland::Server::DataSourceInterface*) #endif diff --git a/src/server/display.cpp b/src/server/display.cpp index ab57f79..1ba6db9 100644 --- a/src/server/display.cpp +++ b/src/server/display.cpp @@ -1,547 +1,576 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "display.h" #include "compositor_interface.h" #include "datadevicemanager_interface.h" #include "dpms_interface.h" #include "outputconfiguration_interface.h" #include "outputmanagement_interface.h" #include "outputdevice_interface.h" #include "idle_interface.h" +#include "idleinhibit_interface_p.h" #include "remote_access_interface.h" #include "fakeinput_interface.h" #include "logging_p.h" #include "output_interface.h" #include "plasmashell_interface.h" #include "plasmawindowmanagement_interface.h" #include "pointerconstraints_interface_p.h" #include "pointergestures_interface_p.h" #include "qtsurfaceextension_interface.h" #include "seat_interface.h" #include "shadow_interface.h" #include "blur_interface.h" #include "contrast_interface.h" #include "relativepointer_interface_p.h" #include "server_decoration_interface.h" #include "slide_interface.h" #include "shell_interface.h" #include "subcompositor_interface.h" #include "textinput_interface_p.h" #include "xdgshell_v5_interface_p.h" #include "xdgforeign_interface.h" #include "xdgshell_v6_interface_p.h" +#include "appmenu_interface.h" +#include "server_decoration_palette_interface.h" #include #include #include #include #include #include #include namespace KWayland { namespace Server { class Display::Private { public: Private(Display *q); void flush(); void dispatch(); void setRunning(bool running); void installSocketNotifier(); wl_display *display = nullptr; wl_event_loop *loop = nullptr; QString socketName = QStringLiteral("wayland-0"); bool running = false; QList outputs; QList outputdevices; QVector seats; QVector clients; EGLDisplay eglDisplay = EGL_NO_DISPLAY; private: Display *q; }; Display::Private::Private(Display *q) : q(q) { } void Display::Private::installSocketNotifier() { if (!QThread::currentThread()) { return; } int fd = wl_event_loop_get_fd(loop); if (fd == -1) { qCWarning(KWAYLAND_SERVER) << "Did not get the file descriptor for the event loop"; return; } QSocketNotifier *m_notifier = new QSocketNotifier(fd, QSocketNotifier::Read, q); QObject::connect(m_notifier, &QSocketNotifier::activated, q, [this] { dispatch(); } ); QObject::connect(QThread::currentThread()->eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, q, [this] { flush(); }); setRunning(true); } Display::Display(QObject *parent) : QObject(parent) , d(new Private(this)) { } Display::~Display() { terminate(); if (d->display) { wl_display_destroy(d->display); } } void Display::Private::flush() { if (!display || !loop) { return; } wl_display_flush_clients(display); } void Display::Private::dispatch() { if (!display || !loop) { return; } if (wl_event_loop_dispatch(loop, 0) != 0) { qCWarning(KWAYLAND_SERVER) << "Error on dispatching Wayland event loop"; } } void Display::setSocketName(const QString &name) { if (d->socketName == name) { return; } d->socketName = name; emit socketNameChanged(d->socketName); } QString Display::socketName() const { return d->socketName; } void Display::start(StartMode mode) { Q_ASSERT(!d->running); Q_ASSERT(!d->display); d->display = wl_display_create(); if (mode == StartMode::ConnectToSocket) { if (wl_display_add_socket(d->display, qPrintable(d->socketName)) != 0) { qCWarning(KWAYLAND_SERVER) << "Failed to create Wayland socket"; return; } } d->loop = wl_display_get_event_loop(d->display); d->installSocketNotifier(); } void Display::startLoop() { Q_ASSERT(!d->running); Q_ASSERT(d->display); d->installSocketNotifier(); } void Display::dispatchEvents(int msecTimeout) { Q_ASSERT(d->display); if (d->running) { d->dispatch(); } else if (d->loop) { wl_event_loop_dispatch(d->loop, msecTimeout); wl_display_flush_clients(d->display); } } void Display::terminate() { if (!d->running) { return; } emit aboutToTerminate(); wl_display_terminate(d->display); wl_display_destroy(d->display); d->display = nullptr; d->loop = nullptr; d->setRunning(false); } void Display::Private::setRunning(bool r) { Q_ASSERT(running != r); running = r; emit q->runningChanged(running); } OutputInterface *Display::createOutput(QObject *parent) { OutputInterface *output = new OutputInterface(this, parent); connect(output, &QObject::destroyed, this, [this,output] { d->outputs.removeAll(output); }); connect(this, &Display::aboutToTerminate, output, [this,output] { removeOutput(output); }); d->outputs << output; return output; } CompositorInterface *Display::createCompositor(QObject *parent) { CompositorInterface *compositor = new CompositorInterface(this, parent); connect(this, &Display::aboutToTerminate, compositor, [this,compositor] { delete compositor; }); return compositor; } ShellInterface *Display::createShell(QObject *parent) { ShellInterface *shell = new ShellInterface(this, parent); connect(this, &Display::aboutToTerminate, shell, [this,shell] { delete shell; }); return shell; } OutputDeviceInterface *Display::createOutputDevice(QObject *parent) { OutputDeviceInterface *output = new OutputDeviceInterface(this, parent); connect(output, &QObject::destroyed, this, [this,output] { d->outputdevices.removeAll(output); }); connect(this, &Display::aboutToTerminate, output, [this,output] { removeOutputDevice(output); }); d->outputdevices << output; return output; } OutputManagementInterface *Display::createOutputManagement(QObject *parent) { OutputManagementInterface *om = new OutputManagementInterface(this, parent); connect(this, &Display::aboutToTerminate, om, [this,om] { delete om; }); return om; } SeatInterface *Display::createSeat(QObject *parent) { SeatInterface *seat = new SeatInterface(this, parent); connect(seat, &QObject::destroyed, this, [this, seat] { d->seats.removeAll(seat); }); connect(this, &Display::aboutToTerminate, seat, [this,seat] { delete seat; }); d->seats << seat; return seat; } SubCompositorInterface *Display::createSubCompositor(QObject *parent) { auto c = new SubCompositorInterface(this, parent); connect(this, &Display::aboutToTerminate, c, [this,c] { delete c; }); return c; } DataDeviceManagerInterface *Display::createDataDeviceManager(QObject *parent) { auto m = new DataDeviceManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, m, [this,m] { delete m; }); return m; } PlasmaShellInterface *Display::createPlasmaShell(QObject* parent) { auto s = new PlasmaShellInterface(this, parent); connect(this, &Display::aboutToTerminate, s, [this, s] { delete s; }); return s; } PlasmaWindowManagementInterface *Display::createPlasmaWindowManagement(QObject *parent) { auto wm = new PlasmaWindowManagementInterface(this, parent); connect(this, &Display::aboutToTerminate, wm, [this, wm] { delete wm; }); return wm; } QtSurfaceExtensionInterface *Display::createQtSurfaceExtension(QObject *parent) { auto s = new QtSurfaceExtensionInterface(this, parent); connect(this, &Display::aboutToTerminate, s, [this, s] { delete s; }); return s; } RemoteAccessManagerInterface *Display::createRemoteAccessManager(QObject *parent) { auto i = new RemoteAccessManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, i, [this, i] { delete i; }); return i; } IdleInterface *Display::createIdle(QObject *parent) { auto i = new IdleInterface(this, parent); connect(this, &Display::aboutToTerminate, i, [this, i] { delete i; }); return i; } FakeInputInterface *Display::createFakeInput(QObject *parent) { auto i = new FakeInputInterface(this, parent); connect(this, &Display::aboutToTerminate, i, [this, i] { delete i; }); return i; } ShadowManagerInterface *Display::createShadowManager(QObject *parent) { auto s = new ShadowManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, s, [this, s] { delete s; }); return s; } BlurManagerInterface *Display::createBlurManager(QObject *parent) { auto b = new BlurManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); return b; } ContrastManagerInterface *Display::createContrastManager(QObject *parent) { auto b = new ContrastManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); return b; } SlideManagerInterface *Display::createSlideManager(QObject *parent) { auto b = new SlideManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); return b; } DpmsManagerInterface *Display::createDpmsManager(QObject *parent) { auto d = new DpmsManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, d, [this, d] { delete d; }); return d; } ServerSideDecorationManagerInterface *Display::createServerSideDecorationManager(QObject *parent) { auto d = new ServerSideDecorationManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, d, [d] { delete d; }); return d; } TextInputManagerInterface *Display::createTextInputManager(const TextInputInterfaceVersion &version, QObject *parent) { TextInputManagerInterface *t = nullptr; switch (version) { case TextInputInterfaceVersion::UnstableV0: t = new TextInputManagerUnstableV0Interface(this, parent); break; case TextInputInterfaceVersion::UnstableV1: // unsupported return nullptr; case TextInputInterfaceVersion::UnstableV2: t = new TextInputManagerUnstableV2Interface(this, parent); } connect(this, &Display::aboutToTerminate, t, [t] { delete t; }); return t; } XdgShellInterface *Display::createXdgShell(const XdgShellInterfaceVersion &version, QObject *parent) { XdgShellInterface *x = nullptr; switch (version) { case XdgShellInterfaceVersion::UnstableV5: x = new XdgShellV5Interface(this, parent); break; case XdgShellInterfaceVersion::UnstableV6: x = new XdgShellV6Interface(this, parent); break; } connect(this, &Display::aboutToTerminate, x, [x] { delete x; }); return x; } RelativePointerManagerInterface *Display::createRelativePointerManager(const RelativePointerInterfaceVersion &version, QObject *parent) { RelativePointerManagerInterface *r = nullptr; switch (version) { case RelativePointerInterfaceVersion::UnstableV1: r = new RelativePointerManagerUnstableV1Interface(this, parent); break; } connect(this, &Display::aboutToTerminate, r, [r] { delete r; }); return r; } PointerGesturesInterface *Display::createPointerGestures(const PointerGesturesInterfaceVersion &version, QObject *parent) { PointerGesturesInterface *p = nullptr; switch (version) { case PointerGesturesInterfaceVersion::UnstableV1: p = new PointerGesturesUnstableV1Interface(this, parent); break; } connect(this, &Display::aboutToTerminate, p, [p] { delete p; }); return p; } PointerConstraintsInterface *Display::createPointerConstraints(const PointerConstraintsInterfaceVersion &version, QObject *parent) { PointerConstraintsInterface *p = nullptr; switch (version) { case PointerConstraintsInterfaceVersion::UnstableV1: p = new PointerConstraintsUnstableV1Interface(this, parent); break; } connect(this, &Display::aboutToTerminate, p, [p] { delete p; }); return p; } XdgForeignInterface *Display::createXdgForeignInterface(QObject *parent) { XdgForeignInterface *foreign = new XdgForeignInterface(this, parent); connect(this, &Display::aboutToTerminate, foreign, [this,foreign] { delete foreign; }); return foreign; } +IdleInhibitManagerInterface *Display::createIdleInhibitManager(const IdleInhibitManagerInterfaceVersion &version, QObject *parent) +{ + IdleInhibitManagerInterface *i = nullptr; + switch (version) { + case IdleInhibitManagerInterfaceVersion::UnstableV1: + i = new IdleInhibitManagerUnstableV1Interface(this, parent); + break; + } + connect(this, &Display::aboutToTerminate, i, [this,i] { delete i; }); + return i; +} + +AppMenuManagerInterface *Display::createAppMenuManagerInterface(QObject *parent) +{ + auto b = new AppMenuManagerInterface(this, parent); + connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); + return b; +} + +ServerSideDecorationPaletteManagerInterface *Display::createServerSideDecorationPaletteManager(QObject *parent) +{ + auto b = new ServerSideDecorationPaletteManagerInterface(this, parent); + connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); + return b; +} + void Display::createShm() { Q_ASSERT(d->display); wl_display_init_shm(d->display); } void Display::removeOutput(OutputInterface *output) { d->outputs.removeAll(output); delete output; } void Display::removeOutputDevice(OutputDeviceInterface *output) { d->outputdevices.removeAll(output); delete output; } quint32 Display::nextSerial() { return wl_display_next_serial(d->display); } quint32 Display::serial() { return wl_display_get_serial(d->display); } bool Display::isRunning() const { return d->running; } Display::operator wl_display*() { return d->display; } Display::operator wl_display*() const { return d->display; } QList< OutputInterface* > Display::outputs() const { return d->outputs; } QList< OutputDeviceInterface* > Display::outputDevices() const { return d->outputdevices; } QVector Display::seats() const { return d->seats; } ClientConnection *Display::getConnection(wl_client *client) { Q_ASSERT(client); auto it = std::find_if(d->clients.constBegin(), d->clients.constEnd(), [client](ClientConnection *c) { return c->client() == client; } ); if (it != d->clients.constEnd()) { return *it; } // no ConnectionData yet, create it auto c = new ClientConnection(client, this); d->clients << c; connect(c, &ClientConnection::disconnected, this, [this] (ClientConnection *c) { const int index = d->clients.indexOf(c); Q_ASSERT(index != -1); d->clients.remove(index); Q_ASSERT(d->clients.indexOf(c) == -1); emit clientDisconnected(c); } ); emit clientConnected(c); return c; } QVector< ClientConnection* > Display::connections() const { return d->clients; } ClientConnection *Display::createClient(int fd) { Q_ASSERT(fd != -1); Q_ASSERT(d->display); wl_client *c = wl_client_create(d->display, fd); if (!c) { return nullptr; } return getConnection(c); } void Display::setEglDisplay(void *display) { if (d->eglDisplay != EGL_NO_DISPLAY) { qCWarning(KWAYLAND_SERVER) << "EGLDisplay cannot be changed"; return; } d->eglDisplay = (EGLDisplay)display; } void *Display::eglDisplay() const { return d->eglDisplay; } } } diff --git a/src/server/display.h b/src/server/display.h index 6ee15b5..c1ff261 100644 --- a/src/server/display.h +++ b/src/server/display.h @@ -1,273 +1,303 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #ifndef WAYLAND_SERVER_DISPLAY_H #define WAYLAND_SERVER_DISPLAY_H #include #include #include #include "clientconnection.h" struct wl_client; struct wl_display; struct wl_event_loop; namespace KWayland { /** * @short KWayland Server. * * This namespace groups all classes related to the Server module. * * The main entry point into the KWayland::Server API is the Display class. * It allows to create a Wayland server and create various global objects on it. * * KWayland::Server is an API to easily create a head-less Wayland server with a * Qt style API. * * @see Display **/ namespace Server { class CompositorInterface; class DataDeviceManagerInterface; class DpmsManagerInterface; class IdleInterface; +enum class IdleInhibitManagerInterfaceVersion; class RemoteAccessManagerInterface; +class IdleInhibitManagerInterface; class FakeInputInterface; class OutputInterface; class OutputDeviceInterface; class OutputConfigurationInterface; class OutputManagementInterface; class PlasmaShellInterface; class PlasmaWindowManagementInterface; class QtSurfaceExtensionInterface; class SeatInterface; class ShadowManagerInterface; class BlurManagerInterface; class ContrastManagerInterface; class ServerSideDecorationManagerInterface; class SlideManagerInterface; class ShellInterface; class SubCompositorInterface; enum class TextInputInterfaceVersion; class TextInputManagerInterface; class XdgShellV5Interface; enum class XdgShellInterfaceVersion; class XdgShellInterface; enum class RelativePointerInterfaceVersion; class RelativePointerManagerInterface; enum class PointerGesturesInterfaceVersion; class PointerGesturesInterface; enum class PointerConstraintsInterfaceVersion; class PointerConstraintsInterface; class XdgForeignInterface; +class AppMenuManagerInterface; +class ServerSideDecorationPaletteManagerInterface; /** * @brief Class holding the Wayland server display loop. * * @todo Improve documentation **/ class KWAYLANDSERVER_EXPORT Display : public QObject { Q_OBJECT Q_PROPERTY(QString socketName READ socketName WRITE setSocketName NOTIFY socketNameChanged) Q_PROPERTY(bool running READ isRunning NOTIFY runningChanged) public: explicit Display(QObject *parent = nullptr); virtual ~Display(); void setSocketName(const QString &name); QString socketName() const; quint32 serial(); quint32 nextSerial(); /** * How to setup the server connection. * @li ConnectToSocket: the server will open the socket identified by the socket name * @li ConnectClientsOnly: only connections through createClient are possible **/ enum class StartMode { ConnectToSocket, ConnectClientsOnly }; void start(StartMode mode = StartMode::ConnectToSocket); void terminate(); /** * Starts the event loop for the server socket. * This method should only be used if start() is used before creating the * QCoreApplication. In that case start() cannot fully setup the event processing * and the loop needs to be started after the QCoreApplication got created. * @see start * @see dispatchEvents **/ void startLoop(); /** * Dispatches pending events in a blocking way. May only be used if the Display is * created and started before the QCoreApplication is created. Once the QCoreApplication * is created and the event loop is started this method delegates to the normal dispatch * handling. * @see startLoop **/ void dispatchEvents(int msecTimeout = -1); /** * Create a client for the given file descriptor. * * The client is created as if it connected through the normal server * socket. This method can be used to create a connection bypassing the * normal socket connection. It's recommended to use together with * socketpair and pass the other side of the socket to the client. * * @param fd The file descriptor for the socket to the client * @returns The new ClientConnection or @c null on failure. **/ ClientConnection *createClient(int fd); operator wl_display*(); operator wl_display*() const; bool isRunning() const; OutputInterface *createOutput(QObject *parent = nullptr); void removeOutput(OutputInterface *output); QList outputs() const; OutputDeviceInterface *createOutputDevice(QObject *parent = nullptr); void removeOutputDevice(OutputDeviceInterface *output); QList outputDevices() const; CompositorInterface *createCompositor(QObject *parent = nullptr); void createShm(); ShellInterface *createShell(QObject *parent = nullptr); SeatInterface *createSeat(QObject *parent = nullptr); /** * @returns All SeatInterface currently managed on the Display. * @since 5.6 **/ QVector seats() const; SubCompositorInterface *createSubCompositor(QObject *parent = nullptr); DataDeviceManagerInterface *createDataDeviceManager(QObject *parent = nullptr); OutputManagementInterface *createOutputManagement(QObject *parent = nullptr); PlasmaShellInterface *createPlasmaShell(QObject *parent = nullptr); PlasmaWindowManagementInterface *createPlasmaWindowManagement(QObject *parent = nullptr); QtSurfaceExtensionInterface *createQtSurfaceExtension(QObject *parent = nullptr); IdleInterface *createIdle(QObject *parent = nullptr); RemoteAccessManagerInterface *createRemoteAccessManager(QObject *parent = nullptr); FakeInputInterface *createFakeInput(QObject *parent = nullptr); ShadowManagerInterface *createShadowManager(QObject *parent = nullptr); BlurManagerInterface *createBlurManager(QObject *parent = nullptr); ContrastManagerInterface *createContrastManager(QObject *parent = nullptr); SlideManagerInterface *createSlideManager(QObject *parent = nullptr); DpmsManagerInterface *createDpmsManager(QObject *parent = nullptr); /** * @since 5.6 **/ ServerSideDecorationManagerInterface *createServerSideDecorationManager(QObject *parent = nullptr); /** * Create the text input manager in interface @p version. * @returns The created manager object * @since 5.23 **/ TextInputManagerInterface *createTextInputManager(const TextInputInterfaceVersion &version, QObject *parent = nullptr); /** * Creates the XdgShell in interface @p version. * * @since 5.25 **/ XdgShellInterface *createXdgShell(const XdgShellInterfaceVersion &version, QObject *parent = nullptr); /** * Creates the RelativePointerManagerInterface in interface @p version * * @returns The created manager object * @since 5.28 **/ RelativePointerManagerInterface *createRelativePointerManager(const RelativePointerInterfaceVersion &version, QObject *parent = nullptr); /** * Creates the PointerGesturesInterface in interface @p version * * @returns The created manager object * @since 5.29 **/ PointerGesturesInterface *createPointerGestures(const PointerGesturesInterfaceVersion &version, QObject *parent = nullptr); /** * Creates the PointerConstraintsInterface in interface @p version * * @returns The created manager object * @since 5.29 **/ PointerConstraintsInterface *createPointerConstraints(const PointerConstraintsInterfaceVersion &version, QObject *parent = nullptr); /** * Creates the XdgForeignInterface in interface @p version * * @returns The created manager object * @since 5.40 **/ XdgForeignInterface *createXdgForeignInterface(QObject *parent = nullptr); + + /** + * Creates the IdleInhibitManagerInterface in interface @p version. + * + * @returns The created manager object + * @since 5.41 + **/ + IdleInhibitManagerInterface *createIdleInhibitManager(const IdleInhibitManagerInterfaceVersion &version, QObject *parent = nullptr); + + /** + * Creates the AppMenuManagerInterface in interface @p version. + * + * @returns The created manager object + * @since 5.42 + **/ + AppMenuManagerInterface *createAppMenuManagerInterface(QObject *parent = nullptr); + + /** + * Creates the ServerSideDecorationPaletteManagerInterface in interface @p version. + * + * @returns The created manager object + * @since 5.42 + **/ + ServerSideDecorationPaletteManagerInterface *createServerSideDecorationPaletteManager(QObject *parent = nullptr); + + /** * Gets the ClientConnection for the given @p client. * If there is no ClientConnection yet for the given @p client, it will be created. * @param client The native client for which the ClientConnection is retrieved * @return The ClientConnection for the given native client **/ ClientConnection *getConnection(wl_client *client); QVector connections() const; /** * Set the EGL @p display for this Wayland display. * The EGLDisplay can only be set once and must be alive as long as the Wayland display * is alive. The user should have set up the binding between the EGLDisplay and the * Wayland display prior to calling this method. * * @see eglDisplay * @since 5.3 **/ void setEglDisplay(void *display); /** * @returns the EGLDisplay used for this Wayland display or EGL_NO_DISPLAY if not set. * @see setEglDisplay * @since 5.3 **/ void *eglDisplay() const; Q_SIGNALS: void socketNameChanged(const QString&); void runningChanged(bool); void aboutToTerminate(); void clientConnected(KWayland::Server::ClientConnection*); void clientDisconnected(KWayland::Server::ClientConnection*); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/server/filtered_display.cpp b/src/server/filtered_display.cpp new file mode 100644 index 0000000..c2e4c97 --- /dev/null +++ b/src/server/filtered_display.cpp @@ -0,0 +1,70 @@ +/******************************************************************** +Copyright 2017 David Edmundson + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +*********************************************************************/ + +#include "filtered_display.h" +#include "display.h" + +#include + +#include + +namespace KWayland +{ +namespace Server +{ + +class FilteredDisplay::Private +{ +public: + Private(FilteredDisplay *_q); + FilteredDisplay *q; + static bool globalFilterCallback(const wl_client *client, const wl_global *global, void *data) + { + auto t = static_cast(data); + auto clientConnection = t->q->getConnection(const_cast(client)); + auto interface = wl_global_get_interface(global); + auto name = QByteArray::fromRawData(interface->name, strlen(interface->name)); + return t->q->allowInterface(clientConnection, name); + }; +}; + +FilteredDisplay::Private::Private(FilteredDisplay *_q): + q(_q) +{} + + +FilteredDisplay::FilteredDisplay(QObject *parent): + Display(parent), + d(new Private(this)) +{ + connect(this, &Display::runningChanged, [this](bool running) { + if (!running) { + return; + } + wl_display_set_global_filter(*this, Private::globalFilterCallback, d.data()); + }); +} + +FilteredDisplay::~FilteredDisplay() +{ +} + +} +} diff --git a/src/server/datadevicemanager_interface.h b/src/server/filtered_display.h similarity index 55% copy from src/server/datadevicemanager_interface.h copy to src/server/filtered_display.h index 25e07d0..1a3babb 100644 --- a/src/server/datadevicemanager_interface.h +++ b/src/server/filtered_display.h @@ -1,60 +1,65 @@ /******************************************************************** -Copyright 2014 Martin Gräßlin +Copyright 2017 David Edmundson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ -#ifndef WAYLAND_SERVER_DATA_DEVICE_MANAGER_INTERFACE_H -#define WAYLAND_SERVER_DATA_DEVICE_MANAGER_INTERFACE_H -#include +#ifndef KWAYLAND_SERVER_FILTERED_DISPLAY_H +#define KWAYLAND_SERVER_FILTERED_DISPLAY_H -#include #include "global.h" -#include "datadevice_interface.h" -#include "datasource_interface.h" +#include "display.h" + +#include namespace KWayland { namespace Server { -class Display; - /** - * @brief Represents the Global for wl_data_device_manager interface. - * - **/ -class KWAYLANDSERVER_EXPORT DataDeviceManagerInterface : public Global +* Server Implementation that allows one to restrict which globals are available to which clients +* +* Users of this class must implement the virtual @method allowInterface method. +* +* @since 5.FIXME +*/ +class KWAYLANDSERVER_EXPORT FilteredDisplay : public Display { Q_OBJECT public: - virtual ~DataDeviceManagerInterface(); - -Q_SIGNALS: - void dataSourceCreated(KWayland::Server::DataSourceInterface*); - void dataDeviceCreated(KWayland::Server::DataDeviceInterface*); + FilteredDisplay(QObject *parent); + ~FilteredDisplay(); +/** +* Return whether the @arg client can see the interface with the given @arg interfaceName +* +* When false will not see these globals for a given interface in the registry, +* and any manual attempts to bind will fail +* +* @return true if the client should be able to access the global with the following interfaceName +*/ + virtual bool allowInterface(ClientConnection *client, const QByteArray &interfaceName) = 0; private: - explicit DataDeviceManagerInterface(Display *display, QObject *parent = nullptr); - friend class Display; class Private; + QScopedPointer d; }; } } #endif diff --git a/src/server/idle_interface.cpp b/src/server/idle_interface.cpp index c9e7ec9..ccacac2 100644 --- a/src/server/idle_interface.cpp +++ b/src/server/idle_interface.cpp @@ -1,200 +1,269 @@ /******************************************************************** Copyright 2015 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "idle_interface.h" #include "display.h" #include "global_p.h" #include "resource_p.h" #include "seat_interface.h" #include +#include #include #include namespace KWayland { namespace Server { class IdleInterface::Private : public Global::Private { public: Private(IdleInterface *q, Display *d); + int inhibitCount = 0; + QVector idleTimeouts; + private: void bind(wl_client *client, uint32_t version, uint32_t id) override; static void getIdleTimeoutCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *seat, uint32_t timeout); static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } IdleInterface *q; static const struct org_kde_kwin_idle_interface s_interface; static const quint32 s_version; }; class IdleTimeoutInterface::Private : public Resource::Private { public: Private(SeatInterface *seat, IdleTimeoutInterface *q, IdleInterface *manager, wl_resource *parentResource); ~Private(); void setup(quint32 timeout); + void simulateUserActivity(); + SeatInterface *seat; QTimer *timer = nullptr; private: static void simulateUserActivityCallback(wl_client *client, wl_resource *resource); IdleTimeoutInterface *q_func() { return reinterpret_cast(q); } static const struct org_kde_kwin_idle_timeout_interface s_interface; }; const quint32 IdleInterface::Private::s_version = 1; #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct org_kde_kwin_idle_interface IdleInterface::Private::s_interface = { getIdleTimeoutCallback }; #endif IdleInterface::Private::Private(IdleInterface *q, Display *d) : Global::Private(d, &org_kde_kwin_idle_interface, s_version) , q(q) { } void IdleInterface::Private::getIdleTimeoutCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *seat, uint32_t timeout) { Private *p = cast(resource); SeatInterface *s = SeatInterface::get(seat); Q_ASSERT(s); IdleTimeoutInterface *idleTimeout = new IdleTimeoutInterface(s, p->q, resource); idleTimeout->create(p->display->getConnection(client), wl_resource_get_version(resource), id); if (!idleTimeout->resource()) { wl_resource_post_no_memory(resource); delete idleTimeout; return; } + p->idleTimeouts << idleTimeout; + QObject::connect(idleTimeout, &IdleTimeoutInterface::aboutToBeUnbound, p->q, + std::bind(&QVector::removeOne, p->idleTimeouts, idleTimeout)); idleTimeout->d_func()->setup(timeout); } void IdleInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&org_kde_kwin_idle_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); // TODO: should we track? } void IdleInterface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) } IdleInterface::IdleInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } IdleInterface::~IdleInterface() = default; +void IdleInterface::inhibit() +{ + Q_D(); + d->inhibitCount++; + if (d->inhibitCount == 1) { + emit inhibitedChanged(); + } +} + +void IdleInterface::uninhibit() +{ + Q_D(); + d->inhibitCount--; + if (d->inhibitCount == 0) { + emit inhibitedChanged(); + } +} + +bool IdleInterface::isInhibited() const +{ + Q_D(); + return d->inhibitCount > 0; +} + +void IdleInterface::simulateUserActivity() +{ + Q_D(); + for (auto i : qAsConst(d->idleTimeouts)) { + i->d_func()->simulateUserActivity(); + } +} + +IdleInterface::Private *IdleInterface::d_func() const +{ + return reinterpret_cast(d.data()); +} + #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct org_kde_kwin_idle_timeout_interface IdleTimeoutInterface::Private::s_interface = { resourceDestroyedCallback, simulateUserActivityCallback }; #endif IdleTimeoutInterface::Private::Private(SeatInterface *seat, IdleTimeoutInterface *q, IdleInterface *manager, wl_resource *parentResource) : Resource::Private(q, manager, parentResource, &org_kde_kwin_idle_timeout_interface, &s_interface) , seat(seat) { } IdleTimeoutInterface::Private::~Private() = default; void IdleTimeoutInterface::Private::simulateUserActivityCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client); Private *p = reinterpret_cast(wl_resource_get_user_data(resource)); - if (!p->timer) { + p->simulateUserActivity(); +} + +void IdleTimeoutInterface::Private::simulateUserActivity() +{ + if (!timer) { // not yet configured return; } - if (!p->timer->isActive() && p->resource) { - org_kde_kwin_idle_timeout_send_resumed(p->resource); + if (qobject_cast(global)->isInhibited()) { + // ignored while inhibited + return; + } + if (!timer->isActive() && resource) { + org_kde_kwin_idle_timeout_send_resumed(resource); } - p->timer->start(); + timer->start(); } void IdleTimeoutInterface::Private::setup(quint32 timeout) { if (timer) { return; } timer = new QTimer(q); timer->setSingleShot(true); // less than 5 sec is not idle by definition timer->setInterval(qMax(timeout, 5000u)); QObject::connect(timer, &QTimer::timeout, q, [this] { if (resource) { org_kde_kwin_idle_timeout_send_idle(resource); } } ); + if (qobject_cast(global)->isInhibited()) { + // don't start if inhibited + return; + } timer->start(); } IdleTimeoutInterface::IdleTimeoutInterface(SeatInterface *seat, IdleInterface *parent, wl_resource *parentResource) : Resource(new Private(seat, this, parent, parentResource)) { connect(seat, &SeatInterface::timestampChanged, this, + [this] { + Q_D(); + d->simulateUserActivity(); + } + ); + connect(parent, &IdleInterface::inhibitedChanged, this, [this] { Q_D(); if (!d->timer) { // not yet configured return; } - if (!d->timer->isActive() && d->resource) { - org_kde_kwin_idle_timeout_send_resumed(d->resource); + if (qobject_cast(d->global)->isInhibited()) { + if (!d->timer->isActive() && d->resource) { + org_kde_kwin_idle_timeout_send_resumed(d->resource); + } + d->timer->stop(); + } else { + d->timer->start(); } - d->timer->start(); } ); } IdleTimeoutInterface::~IdleTimeoutInterface() = default; IdleTimeoutInterface::Private *IdleTimeoutInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/idle_interface.h b/src/server/idle_interface.h index 54099f2..15bb0a5 100644 --- a/src/server/idle_interface.h +++ b/src/server/idle_interface.h @@ -1,84 +1,140 @@ /******************************************************************** Copyright 2015 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #ifndef KWAYLAND_SERVER_IDLE_INTERFACE_H #define KWAYLAND_SERVER_IDLE_INTERFACE_H #include #include "global.h" #include "resource.h" namespace KWayland { namespace Server { class Display; class SeatInterface; /** * @brief Global representing the org_kde_kwin_idle interface. * * The IdleInterface allows to register callbacks which are invoked if there has * not been any user activity (no input) for a specified time span on a seat. * * A client can bind an idle timeout for a SeatInterface and through that register * an idle timeout. The complete interaction is handled internally, thus the API * user only needs to create the IdleInterface in order to provide this feature. * * This interface is useful for clients as it allows them to perform power management, * chat applications might want to set to away after no user input for some time, etc. * * Of course this exposes the global input usage to all clients. Normally clients don't * know whether the input devices are used, only if their surfaces have focus. With this * interface it is possible to notice that there are input events. A server should consider * this to decide whether it wants to provide this feature! * * @since 5.4 **/ class KWAYLANDSERVER_EXPORT IdleInterface : public Global { Q_OBJECT public: virtual ~IdleInterface(); + /** + * Inhibits the IdleInterface. While inhibited no IdleTimeoutInterface interface gets + * notified about an idle timeout. + * + * This can be used to inhibit power management, screen locking, etc. directly from + * Compositor side. + * + * To resume idle timeouts invoke @link{uninhibit}. It is possible to invoke inhibit several + * times, in that case uninhibit needs to called the same amount as inhibit has been called. + * @see uninhibit + * @see isInhibited + * @see inhibitedChanged + * @since 5.41 + **/ + void inhibit(); + + /** + * Unhibits the IdleInterface. The idle timeouts are only restarted if uninhibit has been + * called the same amount as inhibit. + * + * @see inhibit + * @see isInhibited + * @see inhibitedChanged + * @since 5.41 + **/ + void uninhibit(); + + /** + * @returns Whether idle timeouts are currently inhibited + * @see inhibit + * @see uninhibit + * @see inhibitedChanged + * @since 5.41 + **/ + bool isInhibited() const; + + /** + * Calling this method allows the Compositor to simulate user activity. + * This means the same action is performed as if the user interacted with + * an input device on the SeatInterface. + * Idle timeouts are resumed and the idle time gets restarted. + * @since 5.42 + **/ + void simulateUserActivity(); + +Q_SIGNALS: + /** + * Emitted when the system gets inhibited or uninhibited. + * @see inhibit + * @see uninhibit + * @see isInhibited + * @since 5.41 + **/ + void inhibitedChanged(); + private: explicit IdleInterface(Display *display, QObject *parent = nullptr); friend class Display; class Private; + Private *d_func() const; }; // TODO: KF6 make private class class KWAYLANDSERVER_EXPORT IdleTimeoutInterface : public Resource { Q_OBJECT public: virtual ~IdleTimeoutInterface(); private: explicit IdleTimeoutInterface(SeatInterface *seat, IdleInterface *parent, wl_resource *parentResource); friend class IdleInterface; class Private; Private *d_func() const; }; } } #endif diff --git a/src/server/dataoffer_interface.h b/src/server/idleinhibit_interface.cpp similarity index 53% copy from src/server/dataoffer_interface.h copy to src/server/idleinhibit_interface.cpp index 2412fbd..97b46c2 100644 --- a/src/server/dataoffer_interface.h +++ b/src/server/idleinhibit_interface.cpp @@ -1,62 +1,53 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/**************************************************************************** +Copyright 2017 Martin Flöser This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . -*********************************************************************/ -#ifndef WAYLAND_SERVER_DATA_OFFER_INTERFACE_H -#define WAYLAND_SERVER_DATA_OFFER_INTERFACE_H - -#include - -#include - -#include "resource.h" +****************************************************************************/ +#include "idleinhibit_interface_p.h" namespace KWayland { namespace Server { -class DataDeviceInterface; -class DataSourceInterface; +IdleInhibitManagerInterface::Private::Private(IdleInhibitManagerInterface *q, Display *d, const wl_interface *interface, quint32 version, IdleInhibitManagerInterfaceVersion interfaceVersion) + : Global::Private(d, interface, version) + , interfaceVersion(interfaceVersion) + , q(q) +{ +} -/** - * @brief Represents the Resource for the wl_data_offer interface. - * - **/ -class KWAYLANDSERVER_EXPORT DataOfferInterface : public Resource +IdleInhibitManagerInterface::IdleInhibitManagerInterface(Private *d, QObject *parent) + : Global(d, parent) { - Q_OBJECT -public: - virtual ~DataOfferInterface(); +} - void sendAllOffers(); +IdleInhibitManagerInterface::~IdleInhibitManagerInterface() = default; -private: - friend class DataDeviceInterface; - explicit DataOfferInterface(DataSourceInterface *source, DataDeviceInterface *parentInterface, wl_resource *parentResource); +IdleInhibitManagerInterfaceVersion IdleInhibitManagerInterface::interfaceVersion() const +{ + Q_D(); + return d->interfaceVersion; +} - class Private; - Private *d_func() const; -}; +IdleInhibitManagerInterface::Private *IdleInhibitManagerInterface::d_func() const +{ + return reinterpret_cast(d.data()); +} } } - -Q_DECLARE_METATYPE(KWayland::Server::DataOfferInterface*) - -#endif diff --git a/src/server/dataoffer_interface.h b/src/server/idleinhibit_interface.h similarity index 54% copy from src/server/dataoffer_interface.h copy to src/server/idleinhibit_interface.h index 2412fbd..b634770 100644 --- a/src/server/dataoffer_interface.h +++ b/src/server/idleinhibit_interface.h @@ -1,62 +1,77 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/**************************************************************************** +Copyright 2017 Martin Flöser This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . -*********************************************************************/ -#ifndef WAYLAND_SERVER_DATA_OFFER_INTERFACE_H -#define WAYLAND_SERVER_DATA_OFFER_INTERFACE_H +****************************************************************************/ +#ifndef KWAYLAND_SERVER_IDLEINHIBIT_H +#define KWAYLAND_SERVER_IDLEINHIBIT_H -#include +#include "global.h" +#include "resource.h" #include -#include "resource.h" - namespace KWayland { namespace Server { -class DataDeviceInterface; -class DataSourceInterface; +class Display; + +/** + * Enum describing the interface versions the IdleInhibitManagerInterface can support. + * + * @since 5.41 + **/ +enum class IdleInhibitManagerInterfaceVersion { + /** + * zwp_idle_inhibit_manager_v1 + **/ + UnstableV1 +}; /** - * @brief Represents the Resource for the wl_data_offer interface. + * The IdleInhibitorManagerInterface is used by clients to inhibit idle on a + * SurfaceInterface. Whether a SurfaceInterface inhibits idle is exposes through + * @link{SurfaceInterface::inhibitsIdle}. * + * @since 5.41 **/ -class KWAYLANDSERVER_EXPORT DataOfferInterface : public Resource +class KWAYLANDSERVER_EXPORT IdleInhibitManagerInterface : public Global { Q_OBJECT public: - virtual ~DataOfferInterface(); - - void sendAllOffers(); + virtual ~IdleInhibitManagerInterface(); -private: - friend class DataDeviceInterface; - explicit DataOfferInterface(DataSourceInterface *source, DataDeviceInterface *parentInterface, wl_resource *parentResource); + /** + * @returns The interface version used by this IdleInhibitManagerInterface + **/ + IdleInhibitManagerInterfaceVersion interfaceVersion() const; +protected: class Private; + explicit IdleInhibitManagerInterface(Private *d, QObject *parent = nullptr); + +private: Private *d_func() const; }; + } } -Q_DECLARE_METATYPE(KWayland::Server::DataOfferInterface*) - #endif diff --git a/src/server/idleinhibit_interface_p.h b/src/server/idleinhibit_interface_p.h new file mode 100644 index 0000000..8ba5766 --- /dev/null +++ b/src/server/idleinhibit_interface_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +Copyright 2017 Martin Flöser + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#ifndef KWAYLAND_SERVER_IDLEINHIBIT_P_H +#define KWAYLAND_SERVER_IDLEINHIBIT_P_H + +#include "idleinhibit_interface.h" +#include "global_p.h" +#include "resource_p.h" + +#include + +namespace KWayland +{ +namespace Server +{ + +class Q_DECL_HIDDEN IdleInhibitManagerUnstableV1Interface : public IdleInhibitManagerInterface +{ + Q_OBJECT +public: + explicit IdleInhibitManagerUnstableV1Interface(Display *display, QObject *parent = nullptr); + ~IdleInhibitManagerUnstableV1Interface() override; + +private: + class Private; +}; + +class Q_DECL_HIDDEN IdleInhibitManagerInterface::Private : public Global::Private +{ +public: + IdleInhibitManagerInterfaceVersion interfaceVersion; + +protected: + Private(IdleInhibitManagerInterface *q, Display *d, const wl_interface *interface, quint32 version, IdleInhibitManagerInterfaceVersion interfaceVersion); + IdleInhibitManagerInterface *q; +}; + +class Q_DECL_HIDDEN IdleInhibitorInterface : public Resource +{ + Q_OBJECT +public: + explicit IdleInhibitorInterface(IdleInhibitManagerInterface *c, wl_resource *parentResource); + + virtual ~IdleInhibitorInterface(); + + /** + * @returns The interface version used by this IdleInhibitorInterface + **/ + IdleInhibitManagerInterfaceVersion interfaceVersion() const; + +protected: + class Private; + +private: + Private *d_func() const; + friend class IdleInhibitManagerUnstableV1Interface; +}; + +class Q_DECL_HIDDEN IdleInhibitorInterface::Private : public Resource::Private +{ +public: + Private(IdleInhibitorInterface *q, IdleInhibitManagerInterface *m, wl_resource *parentResource); + ~Private(); + +private: + + IdleInhibitorInterface *q_func() { + return reinterpret_cast(q); + } + + static const struct zwp_idle_inhibitor_v1_interface s_interface; +}; + + +} +} + +#endif + diff --git a/src/server/idleinhibit_interface_v1.cpp b/src/server/idleinhibit_interface_v1.cpp new file mode 100644 index 0000000..2920293 --- /dev/null +++ b/src/server/idleinhibit_interface_v1.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +Copyright 2017 Martin Flöser + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#include "idleinhibit_interface_p.h" +#include "display.h" +#include "surface_interface_p.h" + +namespace KWayland +{ +namespace Server +{ + +class Q_DECL_HIDDEN IdleInhibitManagerUnstableV1Interface::Private : public IdleInhibitManagerInterface::Private +{ +public: + Private(IdleInhibitManagerUnstableV1Interface *q, Display *d); + +private: + void bind(wl_client *client, uint32_t version, uint32_t id) override; + + static void unbind(wl_resource *resource); + static Private *cast(wl_resource *r) { + return reinterpret_cast(wl_resource_get_user_data(r)); + } + + static void destroyCallback(wl_client *client, wl_resource *resource); + static void createInhibitorCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface); + + static const struct zwp_idle_inhibit_manager_v1_interface s_interface; + static const quint32 s_version; +}; + +const quint32 IdleInhibitManagerUnstableV1Interface::Private::s_version = 1; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zwp_idle_inhibit_manager_v1_interface IdleInhibitManagerUnstableV1Interface::Private::s_interface = { + destroyCallback, + createInhibitorCallback +}; +#endif + +IdleInhibitManagerUnstableV1Interface::Private::Private(IdleInhibitManagerUnstableV1Interface *q, Display *d) + : IdleInhibitManagerInterface::Private(q, d, &zwp_idle_inhibit_manager_v1_interface, s_version, IdleInhibitManagerInterfaceVersion::UnstableV1) +{ +} + +void IdleInhibitManagerUnstableV1Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) + wl_resource_destroy(resource); +} + +void IdleInhibitManagerUnstableV1Interface::Private::createInhibitorCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface) +{ + auto s = SurfaceInterface::get(surface); + if (!s) { + // send error? + return; + } + auto q = cast(resource); + auto inhibitor = new IdleInhibitorInterface(q->q, resource); + inhibitor->d_func()->create(q->display->getConnection(client), version, id); + s->d_func()->installIdleInhibitor(inhibitor); +} + +void IdleInhibitManagerUnstableV1Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) +{ + auto c = display->getConnection(client); + wl_resource *resource = c->createResource(&zwp_idle_inhibit_manager_v1_interface, qMin(version, s_version), id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &s_interface, this, unbind); + // TODO: should we track? +} + +void IdleInhibitManagerUnstableV1Interface::Private::unbind(wl_resource *resource) +{ + Q_UNUSED(resource) + // TODO: implement? +} + +IdleInhibitManagerUnstableV1Interface::IdleInhibitManagerUnstableV1Interface(Display *display, QObject *parent) + : IdleInhibitManagerInterface(new Private(this, display), parent) +{ +} + +IdleInhibitManagerUnstableV1Interface::~IdleInhibitManagerUnstableV1Interface() = default; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zwp_idle_inhibitor_v1_interface IdleInhibitorInterface::Private::s_interface = { + resourceDestroyedCallback +}; +#endif + +IdleInhibitorInterface::Private::Private(IdleInhibitorInterface *q, IdleInhibitManagerInterface *c, wl_resource *parentResource) + : Resource::Private(q, c, parentResource, &zwp_idle_inhibitor_v1_interface, &s_interface) +{ +} + +IdleInhibitorInterface::Private::~Private() +{ + if (resource) { + wl_resource_destroy(resource); + resource = nullptr; + } +} + +IdleInhibitorInterface::IdleInhibitorInterface(IdleInhibitManagerInterface *m, wl_resource *parentResource) + : Resource(new Private(this, m, parentResource)) +{ +} + +IdleInhibitorInterface::~IdleInhibitorInterface() = default; + +IdleInhibitorInterface::Private *IdleInhibitorInterface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +} +} diff --git a/src/server/seat_interface.cpp b/src/server/seat_interface.cpp index 7a295f7..4029da1 100644 --- a/src/server/seat_interface.cpp +++ b/src/server/seat_interface.cpp @@ -1,1493 +1,1496 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "seat_interface.h" #include "seat_interface_p.h" #include "display.h" #include "datadevice_interface.h" #include "datasource_interface.h" #include "keyboard_interface.h" #include "keyboard_interface_p.h" #include "pointer_interface.h" #include "pointer_interface_p.h" #include "surface_interface.h" #include "textinput_interface_p.h" // Wayland #ifndef WL_SEAT_NAME_SINCE_VERSION #define WL_SEAT_NAME_SINCE_VERSION 2 #endif // linux #include #if HAVE_LINUX_INPUT_H #include #endif #include namespace KWayland { namespace Server { const quint32 SeatInterface::Private::s_version = 4; const qint32 SeatInterface::Private::s_pointerVersion = 3; const qint32 SeatInterface::Private::s_touchVersion = 3; const qint32 SeatInterface::Private::s_keyboardVersion = 4; SeatInterface::Private::Private(SeatInterface *q, Display *display) : Global::Private(display, &wl_seat_interface, s_version) , q(q) { } #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct wl_seat_interface SeatInterface::Private::s_interface = { getPointerCallback, getKeyboardCallback, getTouchCallback }; #endif SeatInterface::SeatInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { Q_D(); connect(this, &SeatInterface::nameChanged, this, [this, d] { for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { d->sendName(*it); } } ); auto sendCapabilitiesAll = [this, d] { for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { d->sendCapabilities(*it); } }; connect(this, &SeatInterface::hasPointerChanged, this, sendCapabilitiesAll); connect(this, &SeatInterface::hasKeyboardChanged, this, sendCapabilitiesAll); connect(this, &SeatInterface::hasTouchChanged, this, sendCapabilitiesAll); } SeatInterface::~SeatInterface() { Q_D(); while (!d->resources.isEmpty()) { wl_resource_destroy(d->resources.takeLast()); } } void SeatInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { wl_resource *r = wl_resource_create(client, &wl_seat_interface, qMin(s_version, version), id); if (!r) { wl_client_post_no_memory(client); return; } resources << r; wl_resource_set_implementation(r, &s_interface, this, unbind); sendCapabilities(r); sendName(r); } void SeatInterface::Private::unbind(wl_resource *r) { cast(r)->resources.removeAll(r); } void SeatInterface::Private::updatePointerButtonSerial(quint32 button, quint32 serial) { auto it = globalPointer.buttonSerials.find(button); if (it == globalPointer.buttonSerials.end()) { globalPointer.buttonSerials.insert(button, serial); return; } it.value() = serial; } void SeatInterface::Private::updatePointerButtonState(quint32 button, Pointer::State state) { auto it = globalPointer.buttonStates.find(button); if (it == globalPointer.buttonStates.end()) { globalPointer.buttonStates.insert(button, state); return; } it.value() = state; } bool SeatInterface::Private::updateKey(quint32 key, Keyboard::State state) { auto it = keys.states.find(key); if (it == keys.states.end()) { keys.states.insert(key, state); return true; } if (it.value() == state) { return false; } it.value() = state; return true; } void SeatInterface::Private::sendName(wl_resource *r) { if (wl_resource_get_version(r) < WL_SEAT_NAME_SINCE_VERSION) { return; } wl_seat_send_name(r, name.toUtf8().constData()); } void SeatInterface::Private::sendCapabilities(wl_resource *r) { uint32_t capabilities = 0; if (pointer) { capabilities |= WL_SEAT_CAPABILITY_POINTER; } if (keyboard) { capabilities |= WL_SEAT_CAPABILITY_KEYBOARD; } if (touch) { capabilities |= WL_SEAT_CAPABILITY_TOUCH; } wl_seat_send_capabilities(r, capabilities); } SeatInterface::Private *SeatInterface::Private::cast(wl_resource *r) { return r ? reinterpret_cast(wl_resource_get_user_data(r)) : nullptr; } namespace { template static T *interfaceForSurface(SurfaceInterface *surface, const QVector &interfaces) { if (!surface) { return nullptr; } for (auto it = interfaces.begin(); it != interfaces.end(); ++it) { if ((*it)->client() == surface->client()) { return (*it); } } return nullptr; } template static QVector interfacesForSurface(SurfaceInterface *surface, const QVector &interfaces) { QVector ret; if (!surface) { return ret; } for (auto it = interfaces.begin(); it != interfaces.end(); ++it) { if ((*it)->client() == surface->client() && (*it)->resource()) { ret << *it; } } return ret; } template static bool forEachInterface(SurfaceInterface *surface, const QVector &interfaces, std::function method) { if (!surface) { return false; } bool calledAtLeastOne = false; for (auto it = interfaces.begin(); it != interfaces.end(); ++it) { if ((*it)->client() == surface->client() && (*it)->resource()) { method(*it); calledAtLeastOne = true; } } return calledAtLeastOne; } } QVector SeatInterface::Private::pointersForSurface(SurfaceInterface *surface) const { return interfacesForSurface(surface, pointers); } QVector SeatInterface::Private::keyboardsForSurface(SurfaceInterface *surface) const { return interfacesForSurface(surface, keyboards); } TouchInterface *SeatInterface::Private::touchForSurface(SurfaceInterface *surface) const { return interfaceForSurface(surface, touchs); } DataDeviceInterface *SeatInterface::Private::dataDeviceForSurface(SurfaceInterface *surface) const { return interfaceForSurface(surface, dataDevices); } TextInputInterface *SeatInterface::Private::textInputForSurface(SurfaceInterface *surface) const { return interfaceForSurface(surface, textInputs); } void SeatInterface::Private::registerDataDevice(DataDeviceInterface *dataDevice) { Q_ASSERT(dataDevice->seat() == q); dataDevices << dataDevice; auto dataDeviceCleanup = [this, dataDevice] { dataDevices.removeOne(dataDevice); if (keys.focus.selection == dataDevice) { keys.focus.selection = nullptr; } if (currentSelection == dataDevice) { // current selection is cleared currentSelection = nullptr; if (keys.focus.selection) { keys.focus.selection->sendClearSelection(); } } }; QObject::connect(dataDevice, &QObject::destroyed, q, dataDeviceCleanup); QObject::connect(dataDevice, &Resource::unbound, q, dataDeviceCleanup); QObject::connect(dataDevice, &DataDeviceInterface::selectionChanged, q, [this, dataDevice] { updateSelection(dataDevice, true); } ); QObject::connect(dataDevice, &DataDeviceInterface::selectionCleared, q, [this, dataDevice] { updateSelection(dataDevice, false); } ); QObject::connect(dataDevice, &DataDeviceInterface::dragStarted, q, [this, dataDevice] { if (q->hasImplicitPointerGrab(dataDevice->dragImplicitGrabSerial())) { drag.mode = Drag::Mode::Pointer; } else { // TODO: touch return; } drag.source = dataDevice; drag.target = dataDevice; drag.surface = dataDevice->origin(); drag.sourcePointer = interfaceForSurface(drag.surface, pointers); // TODO: transformation needs to be either pointer or touch drag.transformation = globalPointer.focus.transformation; drag.destroyConnection = QObject::connect(dataDevice, &QObject::destroyed, q, [this] { endDrag(display->nextSerial()); } ); dataDevice->updateDragTarget(dataDevice->origin(), dataDevice->dragImplicitGrabSerial()); emit q->dragStarted(); emit q->dragSurfaceChanged(); } ); // is the new DataDevice for the current keyoard focus? if (keys.focus.surface && !keys.focus.selection) { // same client? if (keys.focus.surface->client() == dataDevice->client()) { keys.focus.selection = dataDevice; if (currentSelection && currentSelection->selection()) { dataDevice->sendSelection(currentSelection); } } } } void SeatInterface::Private::registerTextInput(TextInputInterface *ti) { // text input version 0 might call this multiple times if (textInputs.contains(ti)) { return; } textInputs << ti; if (textInput.focus.surface && textInput.focus.surface->client() == ti->client()) { // this is a text input for the currently focused text input surface if (!textInput.focus.textInput) { textInput.focus.textInput = ti; ti->d_func()->sendEnter(textInput.focus.surface, textInput.focus.serial); emit q->focusedTextInputChanged(); } } QObject::connect(ti, &QObject::destroyed, q, [this, ti] { textInputs.removeAt(textInputs.indexOf(ti)); if (textInput.focus.textInput == ti) { textInput.focus.textInput = nullptr; emit q->focusedTextInputChanged(); } } ); } void SeatInterface::Private::endDrag(quint32 serial) { auto target = drag.target; QObject::disconnect(drag.destroyConnection); + if (drag.source) { + drag.source->dragSource()->dropPerformed(); + } if (target) { target->drop(); target->updateDragTarget(nullptr, serial); } drag = Drag(); emit q->dragSurfaceChanged(); emit q->dragEnded(); } void SeatInterface::Private::cancelPreviousSelection(DataDeviceInterface *dataDevice) { if (!currentSelection) { return; } if (auto s = currentSelection->selection()) { if (currentSelection != dataDevice) { // only if current selection is not on the same device // that would cancel the newly set source s->cancel(); } } } void SeatInterface::Private::updateSelection(DataDeviceInterface *dataDevice, bool set) { if (keys.focus.surface && (keys.focus.surface->client() == dataDevice->client())) { // cancel the previous selection cancelPreviousSelection(dataDevice); // new selection on a data device belonging to current keyboard focus currentSelection = dataDevice; } if (dataDevice == currentSelection) { // need to send out the selection if (keys.focus.selection) { if (set) { keys.focus.selection->sendSelection(dataDevice); } else { keys.focus.selection->sendClearSelection(); currentSelection = nullptr; } } } } void SeatInterface::setHasKeyboard(bool has) { Q_D(); if (d->keyboard == has) { return; } d->keyboard = has; emit hasKeyboardChanged(d->keyboard); } void SeatInterface::setHasPointer(bool has) { Q_D(); if (d->pointer == has) { return; } d->pointer = has; emit hasPointerChanged(d->pointer); } void SeatInterface::setHasTouch(bool has) { Q_D(); if (d->touch == has) { return; } d->touch = has; emit hasTouchChanged(d->touch); } void SeatInterface::setName(const QString &name) { Q_D(); if (d->name == name) { return; } d->name = name; emit nameChanged(d->name); } void SeatInterface::Private::getPointerCallback(wl_client *client, wl_resource *resource, uint32_t id) { cast(resource)->getPointer(client, resource, id); } void SeatInterface::Private::getPointer(wl_client *client, wl_resource *resource, uint32_t id) { // TODO: only create if seat has pointer? PointerInterface *pointer = new PointerInterface(q, resource); auto clientConnection = display->getConnection(client); pointer->create(clientConnection, qMin(wl_resource_get_version(resource), s_pointerVersion), id); if (!pointer->resource()) { wl_resource_post_no_memory(resource); delete pointer; return; } pointers << pointer; if (globalPointer.focus.surface && globalPointer.focus.surface->client() == clientConnection) { // this is a pointer for the currently focused pointer surface globalPointer.focus.pointers << pointer; pointer->setFocusedSurface(globalPointer.focus.surface, globalPointer.focus.serial); if (globalPointer.focus.pointers.count() == 1) { // got a new pointer emit q->focusedPointerChanged(pointer); } } QObject::connect(pointer, &QObject::destroyed, q, [pointer,this] { pointers.removeAt(pointers.indexOf(pointer)); if (globalPointer.focus.pointers.removeOne(pointer)) { if (globalPointer.focus.pointers.isEmpty()) { emit q->focusedPointerChanged(nullptr); } } } ); emit q->pointerCreated(pointer); } void SeatInterface::Private::getKeyboardCallback(wl_client *client, wl_resource *resource, uint32_t id) { cast(resource)->getKeyboard(client, resource, id); } void SeatInterface::Private::getKeyboard(wl_client *client, wl_resource *resource, uint32_t id) { // TODO: only create if seat has keyboard? KeyboardInterface *keyboard = new KeyboardInterface(q, resource); auto clientConnection = display->getConnection(client); keyboard->create(clientConnection, qMin(wl_resource_get_version(resource), s_keyboardVersion) , id); if (!keyboard->resource()) { wl_resource_post_no_memory(resource); delete keyboard; return; } keyboard->repeatInfo(keys.keyRepeat.charactersPerSecond, keys.keyRepeat.delay); if (keys.keymap.xkbcommonCompatible) { keyboard->setKeymap(keys.keymap.fd, keys.keymap.size); } keyboards << keyboard; if (keys.focus.surface && keys.focus.surface->client() == clientConnection) { // this is a keyboard for the currently focused keyboard surface keys.focus.keyboards << keyboard; keyboard->setFocusedSurface(keys.focus.surface, keys.focus.serial); } QObject::connect(keyboard, &QObject::destroyed, q, [keyboard,this] { keyboards.removeAt(keyboards.indexOf(keyboard)); keys.focus.keyboards.removeOne(keyboard); } ); emit q->keyboardCreated(keyboard); } void SeatInterface::Private::getTouchCallback(wl_client *client, wl_resource *resource, uint32_t id) { cast(resource)->getTouch(client, resource, id); } void SeatInterface::Private::getTouch(wl_client *client, wl_resource *resource, uint32_t id) { // TODO: only create if seat has touch? TouchInterface *touch = new TouchInterface(q, resource); auto clientConnection = display->getConnection(client); touch->create(clientConnection, qMin(wl_resource_get_version(resource), s_touchVersion), id); if (!touch->resource()) { wl_resource_post_no_memory(resource); delete touch; return; } touchs << touch; if (touchInterface.focus.surface && touchInterface.focus.surface->client() == clientConnection) { // this is a touch for the currently focused touch surface if (!touchInterface.focus.touch) { touchInterface.focus.touch = touch; if (!touchInterface.ids.isEmpty()) { // TODO: send out all the points } } } QObject::connect(touch, &QObject::destroyed, q, [touch,this] { touchs.removeAt(touchs.indexOf(touch)); if (touchInterface.focus.touch == touch) { touchInterface.focus.touch = nullptr; } } ); emit q->touchCreated(touch); } QString SeatInterface::name() const { Q_D(); return d->name; } bool SeatInterface::hasPointer() const { Q_D(); return d->pointer; } bool SeatInterface::hasKeyboard() const { Q_D(); return d->keyboard; } bool SeatInterface::hasTouch() const { Q_D(); return d->touch; } SeatInterface *SeatInterface::get(wl_resource *native) { return Private::get(native); } SeatInterface::Private *SeatInterface::d_func() const { return reinterpret_cast(d.data()); } QPointF SeatInterface::pointerPos() const { Q_D(); return d->globalPointer.pos; } void SeatInterface::setPointerPos(const QPointF &pos) { Q_D(); if (d->globalPointer.pos == pos) { return; } d->globalPointer.pos = pos; emit pointerPosChanged(pos); } quint32 SeatInterface::timestamp() const { Q_D(); return d->timestamp; } void SeatInterface::setTimestamp(quint32 time) { Q_D(); if (d->timestamp == time) { return; } d->timestamp = time; emit timestampChanged(time); } void SeatInterface::setDragTarget(SurfaceInterface *surface, const QPointF &globalPosition, const QMatrix4x4 &inputTransformation) { Q_D(); if (surface == d->drag.surface) { // no change return; } const quint32 serial = d->display->nextSerial(); if (d->drag.target) { d->drag.target->updateDragTarget(nullptr, serial); } d->drag.target = d->dataDeviceForSurface(surface); // TODO: update touch if (d->drag.mode == Private::Drag::Mode::Pointer) { setPointerPos(globalPosition); } if (d->drag.target) { d->drag.surface = surface; d->drag.transformation = inputTransformation; d->drag.target->updateDragTarget(surface, serial); } else { d->drag.surface = nullptr; } emit dragSurfaceChanged(); return; } void SeatInterface::setDragTarget(SurfaceInterface *surface, const QMatrix4x4 &inputTransformation) { // TODO: handle touch setDragTarget(surface, pointerPos(), inputTransformation); } SurfaceInterface *SeatInterface::focusedPointerSurface() const { Q_D(); return d->globalPointer.focus.surface; } void SeatInterface::setFocusedPointerSurface(SurfaceInterface *surface, const QPointF &surfacePosition) { QMatrix4x4 m; m.translate(-surfacePosition.x(), -surfacePosition.y()); setFocusedPointerSurface(surface, m); Q_D(); if (d->globalPointer.focus.surface) { d->globalPointer.focus.offset = surfacePosition; } } void SeatInterface::setFocusedPointerSurface(SurfaceInterface *surface, const QMatrix4x4 &transformation) { Q_D(); if (d->drag.mode == Private::Drag::Mode::Pointer) { // ignore return; } const quint32 serial = d->display->nextSerial(); for (auto it = d->globalPointer.focus.pointers.constBegin(), end = d->globalPointer.focus.pointers.constEnd(); it != end; ++it) { (*it)->setFocusedSurface(nullptr, serial); } if (d->globalPointer.focus.surface) { disconnect(d->globalPointer.focus.destroyConnection); } d->globalPointer.focus = Private::Pointer::Focus(); d->globalPointer.focus.surface = surface; auto p = d->pointersForSurface(surface); d->globalPointer.focus.pointers = p; if (d->globalPointer.focus.surface) { d->globalPointer.focus.destroyConnection = connect(surface, &QObject::destroyed, this, [this] { Q_D(); d->globalPointer.focus = Private::Pointer::Focus(); emit focusedPointerChanged(nullptr); } ); d->globalPointer.focus.offset = QPointF(); d->globalPointer.focus.transformation = transformation; d->globalPointer.focus.serial = serial; } if (p.isEmpty()) { emit focusedPointerChanged(nullptr); return; } // TODO: signal with all pointers emit focusedPointerChanged(p.first()); for (auto it = p.constBegin(), end = p.constEnd(); it != end; ++it) { (*it)->setFocusedSurface(surface, serial); } } PointerInterface *SeatInterface::focusedPointer() const { Q_D(); if (d->globalPointer.focus.pointers.isEmpty()) { return nullptr; } return d->globalPointer.focus.pointers.first(); } void SeatInterface::setFocusedPointerSurfacePosition(const QPointF &surfacePosition) { Q_D(); if (d->globalPointer.focus.surface) { d->globalPointer.focus.offset = surfacePosition; d->globalPointer.focus.transformation = QMatrix4x4(); d->globalPointer.focus.transformation.translate(-surfacePosition.x(), -surfacePosition.y()); } } QPointF SeatInterface::focusedPointerSurfacePosition() const { Q_D(); return d->globalPointer.focus.offset; } void SeatInterface::setFocusedPointerSurfaceTransformation(const QMatrix4x4 &transformation) { Q_D(); if (d->globalPointer.focus.surface) { d->globalPointer.focus.transformation = transformation; } } QMatrix4x4 SeatInterface::focusedPointerSurfaceTransformation() const { Q_D(); return d->globalPointer.focus.transformation; } namespace { static quint32 qtToWaylandButton(Qt::MouseButton button) { #if HAVE_LINUX_INPUT_H static const QHash s_buttons({ {Qt::LeftButton, BTN_LEFT}, {Qt::RightButton, BTN_RIGHT}, {Qt::MiddleButton, BTN_MIDDLE}, {Qt::ExtraButton1, BTN_BACK}, // note: QtWayland maps BTN_SIDE {Qt::ExtraButton2, BTN_FORWARD}, // note: QtWayland maps BTN_EXTRA {Qt::ExtraButton3, BTN_TASK}, // note: QtWayland maps BTN_FORWARD {Qt::ExtraButton4, BTN_EXTRA}, // note: QtWayland maps BTN_BACK {Qt::ExtraButton5, BTN_SIDE}, // note: QtWayland maps BTN_TASK {Qt::ExtraButton6, BTN_TASK + 1}, {Qt::ExtraButton7, BTN_TASK + 2}, {Qt::ExtraButton8, BTN_TASK + 3}, {Qt::ExtraButton9, BTN_TASK + 4}, {Qt::ExtraButton10, BTN_TASK + 5}, {Qt::ExtraButton11, BTN_TASK + 6}, {Qt::ExtraButton12, BTN_TASK + 7}, {Qt::ExtraButton13, BTN_TASK + 8} // further mapping not possible, 0x120 is BTN_JOYSTICK }); return s_buttons.value(button, 0); #else return 0; #endif } } bool SeatInterface::isPointerButtonPressed(Qt::MouseButton button) const { return isPointerButtonPressed(qtToWaylandButton(button)); } bool SeatInterface::isPointerButtonPressed(quint32 button) const { Q_D(); auto it = d->globalPointer.buttonStates.constFind(button); if (it == d->globalPointer.buttonStates.constEnd()) { return false; } return it.value() == Private::Pointer::State::Pressed ? true : false; } void SeatInterface::pointerAxis(Qt::Orientation orientation, quint32 delta) { Q_D(); if (d->drag.mode == Private::Drag::Mode::Pointer) { // ignore return; } if (d->globalPointer.focus.surface) { for (auto it = d->globalPointer.focus.pointers.constBegin(), end = d->globalPointer.focus.pointers.constEnd(); it != end; ++it) { (*it)->axis(orientation, delta); } } } void SeatInterface::pointerButtonPressed(Qt::MouseButton button) { const quint32 nativeButton = qtToWaylandButton(button); if (nativeButton == 0) { return; } pointerButtonPressed(nativeButton); } void SeatInterface::pointerButtonPressed(quint32 button) { Q_D(); const quint32 serial = d->display->nextSerial(); d->updatePointerButtonSerial(button, serial); d->updatePointerButtonState(button, Private::Pointer::State::Pressed); if (d->drag.mode == Private::Drag::Mode::Pointer) { // ignore return; } if (d->globalPointer.focus.surface) { for (auto it = d->globalPointer.focus.pointers.constBegin(), end = d->globalPointer.focus.pointers.constEnd(); it != end; ++it) { (*it)->buttonPressed(button, serial); } if (d->globalPointer.focus.surface == d->keys.focus.surface) { // update the focused child surface auto p = focusedPointer(); if (p) { for (auto it = d->keys.focus.keyboards.constBegin(), end = d->keys.focus.keyboards.constEnd(); it != end; ++it) { (*it)->d_func()->focusChildSurface(p->d_func()->focusedChildSurface, serial); } } } } } void SeatInterface::pointerButtonReleased(Qt::MouseButton button) { const quint32 nativeButton = qtToWaylandButton(button); if (nativeButton == 0) { return; } pointerButtonReleased(nativeButton); } void SeatInterface::pointerButtonReleased(quint32 button) { Q_D(); const quint32 serial = d->display->nextSerial(); const quint32 currentButtonSerial = pointerButtonSerial(button); d->updatePointerButtonSerial(button, serial); d->updatePointerButtonState(button, Private::Pointer::State::Released); if (d->drag.mode == Private::Drag::Mode::Pointer) { if (d->drag.source->dragImplicitGrabSerial() != currentButtonSerial) { // not our drag button - ignore return; } d->endDrag(serial); return; } if (d->globalPointer.focus.surface) { for (auto it = d->globalPointer.focus.pointers.constBegin(), end = d->globalPointer.focus.pointers.constEnd(); it != end; ++it) { (*it)->buttonReleased(button, serial); } } } quint32 SeatInterface::pointerButtonSerial(Qt::MouseButton button) const { return pointerButtonSerial(qtToWaylandButton(button)); } quint32 SeatInterface::pointerButtonSerial(quint32 button) const { Q_D(); auto it = d->globalPointer.buttonSerials.constFind(button); if (it == d->globalPointer.buttonSerials.constEnd()) { return 0; } return it.value(); } void SeatInterface::relativePointerMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds) { Q_D(); if (d->globalPointer.focus.surface) { for (auto it = d->globalPointer.focus.pointers.constBegin(), end = d->globalPointer.focus.pointers.constEnd(); it != end; ++it) { (*it)->relativeMotion(delta, deltaNonAccelerated, microseconds); } } } void SeatInterface::startPointerSwipeGesture(quint32 fingerCount) { Q_D(); if (!d->globalPointer.gestureSurface.isNull()) { return; } d->globalPointer.gestureSurface = QPointer(d->globalPointer.focus.surface); if (d->globalPointer.gestureSurface.isNull()) { return; } const quint32 serial = d->display->nextSerial(); forEachInterface(d->globalPointer.gestureSurface.data(), d->pointers, [serial, fingerCount] (PointerInterface *p) { p->d_func()->startSwipeGesture(serial, fingerCount); } ); } void SeatInterface::updatePointerSwipeGesture(const QSizeF &delta) { Q_D(); if (d->globalPointer.gestureSurface.isNull()) { return; } forEachInterface(d->globalPointer.gestureSurface.data(), d->pointers, [delta] (PointerInterface *p) { p->d_func()->updateSwipeGesture(delta); } ); } void SeatInterface::endPointerSwipeGesture() { Q_D(); if (d->globalPointer.gestureSurface.isNull()) { return; } const quint32 serial = d->display->nextSerial(); forEachInterface(d->globalPointer.gestureSurface.data(), d->pointers, [serial] (PointerInterface *p) { p->d_func()->endSwipeGesture(serial); } ); d->globalPointer.gestureSurface.clear(); } void SeatInterface::cancelPointerSwipeGesture() { Q_D(); if (d->globalPointer.gestureSurface.isNull()) { return; } const quint32 serial = d->display->nextSerial(); forEachInterface(d->globalPointer.gestureSurface.data(), d->pointers, [serial] (PointerInterface *p) { p->d_func()->cancelSwipeGesture(serial); } ); d->globalPointer.gestureSurface.clear(); } void SeatInterface::startPointerPinchGesture(quint32 fingerCount) { Q_D(); if (!d->globalPointer.gestureSurface.isNull()) { return; } d->globalPointer.gestureSurface = QPointer(d->globalPointer.focus.surface); if (d->globalPointer.gestureSurface.isNull()) { return; } const quint32 serial = d->display->nextSerial(); forEachInterface(d->globalPointer.gestureSurface.data(), d->pointers, [serial, fingerCount] (PointerInterface *p) { p->d_func()->startPinchGesture(serial, fingerCount); } ); } void SeatInterface::updatePointerPinchGesture(const QSizeF &delta, qreal scale, qreal rotation) { Q_D(); if (d->globalPointer.gestureSurface.isNull()) { return; } forEachInterface(d->globalPointer.gestureSurface.data(), d->pointers, [delta, scale, rotation] (PointerInterface *p) { p->d_func()->updatePinchGesture(delta, scale, rotation); } ); } void SeatInterface::endPointerPinchGesture() { Q_D(); if (d->globalPointer.gestureSurface.isNull()) { return; } const quint32 serial = d->display->nextSerial(); forEachInterface(d->globalPointer.gestureSurface.data(), d->pointers, [serial] (PointerInterface *p) { p->d_func()->endPinchGesture(serial); } ); d->globalPointer.gestureSurface.clear(); } void SeatInterface::cancelPointerPinchGesture() { Q_D(); if (d->globalPointer.gestureSurface.isNull()) { return; } const quint32 serial = d->display->nextSerial(); forEachInterface(d->globalPointer.gestureSurface.data(), d->pointers, [serial] (PointerInterface *p) { p->d_func()->cancelPinchGesture(serial); } ); d->globalPointer.gestureSurface.clear(); } void SeatInterface::keyPressed(quint32 key) { Q_D(); d->keys.lastStateSerial = d->display->nextSerial(); if (!d->updateKey(key, Private::Keyboard::State::Pressed)) { return; } if (d->keys.focus.surface) { for (auto it = d->keys.focus.keyboards.constBegin(), end = d->keys.focus.keyboards.constEnd(); it != end; ++it) { (*it)->keyPressed(key, d->keys.lastStateSerial); } } } void SeatInterface::keyReleased(quint32 key) { Q_D(); d->keys.lastStateSerial = d->display->nextSerial(); if (!d->updateKey(key, Private::Keyboard::State::Released)) { return; } if (d->keys.focus.surface) { for (auto it = d->keys.focus.keyboards.constBegin(), end = d->keys.focus.keyboards.constEnd(); it != end; ++it) { (*it)->keyReleased(key, d->keys.lastStateSerial); } } } SurfaceInterface *SeatInterface::focusedKeyboardSurface() const { Q_D(); return d->keys.focus.surface; } void SeatInterface::setFocusedKeyboardSurface(SurfaceInterface *surface) { Q_D(); const quint32 serial = d->display->nextSerial(); for (auto it = d->keys.focus.keyboards.constBegin(), end = d->keys.focus.keyboards.constEnd(); it != end; ++it) { (*it)->setFocusedSurface(nullptr, serial); } if (d->keys.focus.surface) { disconnect(d->keys.focus.destroyConnection); } d->keys.focus = Private::Keyboard::Focus(); d->keys.focus.surface = surface; d->keys.focus.keyboards = d->keyboardsForSurface(surface); if (d->keys.focus.surface) { d->keys.focus.destroyConnection = connect(surface, &QObject::destroyed, this, [this] { Q_D(); d->keys.focus = Private::Keyboard::Focus(); } ); d->keys.focus.serial = serial; // selection? d->keys.focus.selection = d->dataDeviceForSurface(surface); if (d->keys.focus.selection) { if (d->currentSelection && d->currentSelection->selection()) { d->keys.focus.selection->sendSelection(d->currentSelection); } else { d->keys.focus.selection->sendClearSelection(); } } } for (auto it = d->keys.focus.keyboards.constBegin(), end = d->keys.focus.keyboards.constEnd(); it != end; ++it) { (*it)->setFocusedSurface(surface, serial); } // focused text input surface follows keyboard if (hasKeyboard()) { setFocusedTextInputSurface(surface); } } void SeatInterface::setKeymap(int fd, quint32 size) { Q_D(); d->keys.keymap.xkbcommonCompatible = true; d->keys.keymap.fd = fd; d->keys.keymap.size = size; for (auto it = d->keyboards.constBegin(); it != d->keyboards.constEnd(); ++it) { (*it)->setKeymap(fd, size); } } void SeatInterface::updateKeyboardModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group) { Q_D(); bool changed = false; #define UPDATE( value ) \ if (d->keys.modifiers.value != value) { \ d->keys.modifiers.value = value; \ changed = true; \ } UPDATE(depressed) UPDATE(latched) UPDATE(locked) UPDATE(group) if (!changed) { return; } const quint32 serial = d->display->nextSerial(); d->keys.modifiers.serial = serial; if (d->keys.focus.surface) { for (auto it = d->keys.focus.keyboards.constBegin(), end = d->keys.focus.keyboards.constEnd(); it != end; ++it) { (*it)->updateModifiers(depressed, latched, locked, group, serial); } } } void SeatInterface::setKeyRepeatInfo(qint32 charactersPerSecond, qint32 delay) { Q_D(); d->keys.keyRepeat.charactersPerSecond = qMax(charactersPerSecond, 0); d->keys.keyRepeat.delay = qMax(delay, 0); for (auto it = d->keyboards.constBegin(); it != d->keyboards.constEnd(); ++it) { (*it)->repeatInfo(d->keys.keyRepeat.charactersPerSecond, d->keys.keyRepeat.delay); } } qint32 SeatInterface::keyRepeatDelay() const { Q_D(); return d->keys.keyRepeat.delay; } qint32 SeatInterface::keyRepeatRate() const { Q_D(); return d->keys.keyRepeat.charactersPerSecond; } bool SeatInterface::isKeymapXkbCompatible() const { Q_D(); return d->keys.keymap.xkbcommonCompatible; } int SeatInterface::keymapFileDescriptor() const { Q_D(); return d->keys.keymap.fd; } quint32 SeatInterface::keymapSize() const { Q_D(); return d->keys.keymap.size; } quint32 SeatInterface::depressedModifiers() const { Q_D(); return d->keys.modifiers.depressed; } quint32 SeatInterface::groupModifiers() const { Q_D(); return d->keys.modifiers.group; } quint32 SeatInterface::latchedModifiers() const { Q_D(); return d->keys.modifiers.latched; } quint32 SeatInterface::lockedModifiers() const { Q_D(); return d->keys.modifiers.locked; } quint32 SeatInterface::lastModifiersSerial() const { Q_D(); return d->keys.modifiers.serial; } QVector< quint32 > SeatInterface::pressedKeys() const { Q_D(); QVector keys; for (auto it = d->keys.states.begin(); it != d->keys.states.end(); ++it) { if (it.value() == Private::Keyboard::State::Pressed) { keys << it.key(); } } return keys; } KeyboardInterface *SeatInterface::focusedKeyboard() const { Q_D(); if (d->keys.focus.keyboards.isEmpty()) { return nullptr; } return d->keys.focus.keyboards.first(); } void SeatInterface::cancelTouchSequence() { Q_D(); if (d->touchInterface.focus.touch) { d->touchInterface.focus.touch->cancel(); } d->touchInterface.ids.clear(); } TouchInterface *SeatInterface::focusedTouch() const { Q_D(); return d->touchInterface.focus.touch; } SurfaceInterface *SeatInterface::focusedTouchSurface() const { Q_D(); return d->touchInterface.focus.surface; } QPointF SeatInterface::focusedTouchSurfacePosition() const { Q_D(); return d->touchInterface.focus.offset; } bool SeatInterface::isTouchSequence() const { Q_D(); return !d->touchInterface.ids.isEmpty(); } void SeatInterface::setFocusedTouchSurface(SurfaceInterface *surface, const QPointF &surfacePosition) { if (isTouchSequence()) { // changing surface not allowed during a touch sequence return; } Q_D(); if (d->touchInterface.focus.surface) { disconnect(d->touchInterface.focus.destroyConnection); } d->touchInterface.focus = Private::Touch::Focus(); d->touchInterface.focus.surface = surface; d->touchInterface.focus.offset = surfacePosition; TouchInterface *t = d->touchForSurface(surface); if (t && !t->resource()) { t = nullptr; } d->touchInterface.focus.touch = t; if (d->touchInterface.focus.surface) { d->touchInterface.focus.destroyConnection = connect(surface, &QObject::destroyed, this, [this] { Q_D(); if (isTouchSequence() && d->touchInterface.focus.touch) { // Surface destroyed during touch sequence - send a cancel d->touchInterface.focus.touch->cancel(); } d->touchInterface.focus = Private::Touch::Focus(); } ); } } void SeatInterface::setFocusedTouchSurfacePosition(const QPointF &surfacePosition) { Q_D(); d->touchInterface.focus.offset = surfacePosition; } qint32 SeatInterface::touchDown(const QPointF &globalPosition) { Q_D(); const qint32 id = d->touchInterface.ids.isEmpty() ? 0 : d->touchInterface.ids.last() + 1; const quint32 serial = display()->nextSerial(); if (d->touchInterface.focus.touch && d->touchInterface.focus.surface) { d->touchInterface.focus.touch->down(id, serial, globalPosition - d->touchInterface.focus.offset); } else if (id == 0 && focusedTouchSurface()) { #if HAVE_LINUX_INPUT_H const QPointF pos = globalPosition - d->touchInterface.focus.offset; const bool result = forEachInterface(focusedTouchSurface(), d->pointers, [this, pos, serial] (PointerInterface *p) { wl_pointer_send_enter(p->resource(), serial, focusedTouchSurface()->resource(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y())); wl_pointer_send_motion(p->resource(), timestamp(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y())); wl_pointer_send_button(p->resource(), serial, timestamp(), BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); } ); if (!result) { return id; } #endif } d->touchInterface.ids << id; return id; } void SeatInterface::touchMove(qint32 id, const QPointF &globalPosition) { Q_D(); if (d->touchInterface.focus.touch && d->touchInterface.focus.surface) { d->touchInterface.focus.touch->move(id, globalPosition - d->touchInterface.focus.offset); } else if (id == 0 && focusedTouchSurface()) { const QPointF pos = globalPosition - d->touchInterface.focus.offset; forEachInterface(focusedTouchSurface(), d->pointers, [this, pos] (PointerInterface *p) { wl_pointer_send_motion(p->resource(), timestamp(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y())); } ); } } void SeatInterface::touchUp(qint32 id) { Q_D(); Q_ASSERT(d->touchInterface.ids.contains(id)); if (d->touchInterface.focus.touch && d->touchInterface.focus.surface) { d->touchInterface.focus.touch->up(id, display()->nextSerial()); } else if (id == 0 && focusedTouchSurface()) { #if HAVE_LINUX_INPUT_H const quint32 serial = display()->nextSerial(); forEachInterface(focusedTouchSurface(), d->pointers, [this, serial] (PointerInterface *p) { wl_pointer_send_button(p->resource(), serial, timestamp(), BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); } ); #endif } d->touchInterface.ids.removeAll(id); } void SeatInterface::touchFrame() { Q_D(); if (d->touchInterface.focus.touch && d->touchInterface.focus.surface) { d->touchInterface.focus.touch->frame(); } } bool SeatInterface::isDrag() const { Q_D(); return d->drag.mode != Private::Drag::Mode::None; } bool SeatInterface::isDragPointer() const { Q_D(); return d->drag.mode == Private::Drag::Mode::Pointer; } bool SeatInterface::isDragTouch() const { Q_D(); return d->drag.mode == Private::Drag::Mode::Touch; } bool SeatInterface::hasImplicitPointerGrab(quint32 serial) const { Q_D(); const auto &serials = d->globalPointer.buttonSerials; for (auto it = serials.begin(), end = serials.end(); it != end; it++) { if (it.value() == serial) { return isPointerButtonPressed(it.key()); } } return false; } QMatrix4x4 SeatInterface::dragSurfaceTransformation() const { Q_D(); return d->drag.transformation; } SurfaceInterface *SeatInterface::dragSurface() const { Q_D(); return d->drag.surface; } PointerInterface *SeatInterface::dragPointer() const { Q_D(); if (d->drag.mode != Private::Drag::Mode::Pointer) { return nullptr; } return d->drag.sourcePointer; } DataDeviceInterface *SeatInterface::dragSource() const { Q_D(); return d->drag.source; } void SeatInterface::setFocusedTextInputSurface(SurfaceInterface *surface) { Q_D(); const quint32 serial = d->display->nextSerial(); const auto old = d->textInput.focus.textInput; if (d->textInput.focus.textInput) { // TODO: setFocusedSurface like in other interfaces d->textInput.focus.textInput->d_func()->sendLeave(serial, d->textInput.focus.surface); } if (d->textInput.focus.surface) { disconnect(d->textInput.focus.destroyConnection); } d->textInput.focus = Private::TextInput::Focus(); d->textInput.focus.surface = surface; TextInputInterface *t = d->textInputForSurface(surface); if (t && !t->resource()) { t = nullptr; } d->textInput.focus.textInput = t; if (d->textInput.focus.surface) { d->textInput.focus.destroyConnection = connect(surface, &Resource::aboutToBeUnbound, this, [this] { setFocusedTextInputSurface(nullptr); } ); d->textInput.focus.serial = serial; } if (t) { // TODO: setFocusedSurface like in other interfaces t->d_func()->sendEnter(surface, serial); } if (old != t) { emit focusedTextInputChanged(); } } SurfaceInterface *SeatInterface::focusedTextInputSurface() const { Q_D(); return d->textInput.focus.surface; } TextInputInterface *SeatInterface::focusedTextInput() const { Q_D(); return d->textInput.focus.textInput; } DataDeviceInterface *SeatInterface::selection() const { Q_D(); return d->currentSelection; } void SeatInterface::setSelection(DataDeviceInterface *dataDevice) { Q_D(); if (d->currentSelection == dataDevice) { return; } // cancel the previous selection d->cancelPreviousSelection(dataDevice); d->currentSelection = dataDevice; if (d->keys.focus.selection) { if (dataDevice && dataDevice->selection()) { d->keys.focus.selection->sendSelection(dataDevice); } else { d->keys.focus.selection->sendClearSelection(); } } } } } diff --git a/src/server/server_decoration_palette_interface.cpp b/src/server/server_decoration_palette_interface.cpp new file mode 100644 index 0000000..f86b1d5 --- /dev/null +++ b/src/server/server_decoration_palette_interface.cpp @@ -0,0 +1,216 @@ +/**************************************************************************** +Copyright 2017 David Edmundson + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#include "server_decoration_palette_interface.h" +#include "display.h" +#include "surface_interface.h" +#include "global_p.h" +#include "resource_p.h" +#include "logging_p.h" + +#include + +#include + +namespace KWayland +{ +namespace Server +{ +class ServerSideDecorationPaletteManagerInterface::Private : public Global::Private +{ +public: + Private(ServerSideDecorationPaletteManagerInterface *q, Display *d); + + QVector palettes; +private: + void bind(wl_client *client, uint32_t version, uint32_t id) override; + + static void unbind(wl_resource *resource); + static Private *cast(wl_resource *r) { + return reinterpret_cast(wl_resource_get_user_data(r)); + } + + static void createCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface); + + ServerSideDecorationPaletteManagerInterface *q; + static const struct org_kde_kwin_server_decoration_palette_manager_interface s_interface; + static const quint32 s_version; +}; + +const quint32 ServerSideDecorationPaletteManagerInterface::Private::s_version = 1; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct org_kde_kwin_server_decoration_palette_manager_interface ServerSideDecorationPaletteManagerInterface::Private::s_interface = { + createCallback +}; +#endif + +void ServerSideDecorationPaletteManagerInterface::Private::createCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface) +{ + auto p = reinterpret_cast(wl_resource_get_user_data(resource)); + Q_ASSERT(p); + + SurfaceInterface *s = SurfaceInterface::get(surface); + if (!s) { + // TODO: send error? + qCWarning(KWAYLAND_SERVER) << "ServerSideDecorationPaletteInterface requested for non existing SurfaceInterface"; + return; + } + auto palette = new ServerSideDecorationPaletteInterface(p->q, s, resource); + palette->create(p->display->getConnection(client), wl_resource_get_version(resource), id); + if (!palette->resource()) { + wl_resource_post_no_memory(resource); + delete palette; + return; + } + p->palettes.append(palette); + QObject::connect(palette, &QObject::destroyed, p->q, [=]() { + p->palettes.removeOne(palette); + }); + emit p->q->paletteCreated(palette); +} + +ServerSideDecorationPaletteManagerInterface::Private::Private(ServerSideDecorationPaletteManagerInterface *q, Display *d) + : Global::Private(d, &org_kde_kwin_server_decoration_palette_manager_interface, s_version) + , q(q) +{ +} + +void ServerSideDecorationPaletteManagerInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) +{ + auto c = display->getConnection(client); + wl_resource *resource = c->createResource(&org_kde_kwin_server_decoration_palette_manager_interface, qMin(version, s_version), id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &s_interface, this, unbind); +} + +void ServerSideDecorationPaletteManagerInterface::Private::unbind(wl_resource *resource) +{ + Q_UNUSED(resource) +} + +class ServerSideDecorationPaletteInterface::Private : public Resource::Private +{ +public: + Private(ServerSideDecorationPaletteInterface *q, ServerSideDecorationPaletteManagerInterface *c, SurfaceInterface *surface, wl_resource *parentResource); + ~Private(); + + + SurfaceInterface *surface; + QString palette; +private: + static void setPaletteCallback(wl_client *client, wl_resource *resource, const char * palette); + + ServerSideDecorationPaletteInterface *q_func() { + return reinterpret_cast(q); + } + static ServerSideDecorationPaletteInterface *get(SurfaceInterface *s); + static const struct org_kde_kwin_server_decoration_palette_interface s_interface; +}; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct org_kde_kwin_server_decoration_palette_interface ServerSideDecorationPaletteInterface::Private::s_interface = { + setPaletteCallback, + resourceDestroyedCallback +}; +#endif + +void ServerSideDecorationPaletteInterface::Private::setPaletteCallback(wl_client *client, wl_resource *resource, const char * palette) +{ + Q_UNUSED(client); + auto p = reinterpret_cast(wl_resource_get_user_data(resource)); + Q_ASSERT(p); + + if (p->palette == QLatin1String(palette)) { + return; + } + p->palette = QString::fromUtf8(palette); + emit p->q_func()->paletteChanged(p->palette); +} + +ServerSideDecorationPaletteInterface::Private::Private(ServerSideDecorationPaletteInterface *q, ServerSideDecorationPaletteManagerInterface *c, SurfaceInterface *s, wl_resource *parentResource) + : Resource::Private(q, c, parentResource, &org_kde_kwin_server_decoration_palette_interface, &s_interface), + surface(s) +{ +} + +ServerSideDecorationPaletteInterface::Private::~Private() +{ + if (resource) { + wl_resource_destroy(resource); + resource = nullptr; + } +} + +ServerSideDecorationPaletteManagerInterface::ServerSideDecorationPaletteManagerInterface(Display *display, QObject *parent) + : Global(new Private(this, display), parent) +{ +} + +ServerSideDecorationPaletteManagerInterface::~ServerSideDecorationPaletteManagerInterface() +{ +} + +ServerSideDecorationPaletteManagerInterface::Private *ServerSideDecorationPaletteManagerInterface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +ServerSideDecorationPaletteInterface* ServerSideDecorationPaletteManagerInterface::paletteForSurface(SurfaceInterface *surface) +{ + Q_D(); + for (ServerSideDecorationPaletteInterface* menu: d->palettes) { + if (menu->surface() == surface) { + return menu; + } + } + return nullptr; +} + +ServerSideDecorationPaletteInterface::ServerSideDecorationPaletteInterface(ServerSideDecorationPaletteManagerInterface *parent, SurfaceInterface *s, wl_resource *parentResource): + Resource(new Private(this, parent, s, parentResource)) +{ +} + +ServerSideDecorationPaletteInterface::Private *ServerSideDecorationPaletteInterface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +ServerSideDecorationPaletteInterface::~ServerSideDecorationPaletteInterface() +{} + +QString ServerSideDecorationPaletteInterface::palette() const +{ + Q_D(); + return d->palette; +} + +SurfaceInterface* ServerSideDecorationPaletteInterface::surface() const +{ + Q_D(); + return d->surface; +} + +}//namespace +} + diff --git a/src/server/server_decoration_palette_interface.h b/src/server/server_decoration_palette_interface.h new file mode 100644 index 0000000..2ec3346 --- /dev/null +++ b/src/server/server_decoration_palette_interface.h @@ -0,0 +1,106 @@ +/**************************************************************************** +Copyright 2017 David Edmundson + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#ifndef KWAYLAND_SERVER_DECORATION_PALETTE_H +#define KWAYLAND_SERVER_DECORATION_PALETTE_H + +#include "global.h" +#include "resource.h" + +#include + +namespace KWayland +{ +namespace Server +{ + +class Display; +class SurfaceInterface; +class ServerSideDecorationPaletteInterface; + +/** + * Allows a client to specify a preferred palette to use for server-side window decorations + * + * This global can be used for clients to bind ServerSideDecorationPaletteInterface instances + * and notifies when a new one is created + * @since 5.42 + */ +class KWAYLANDSERVER_EXPORT ServerSideDecorationPaletteManagerInterface : public Global +{ + Q_OBJECT +public: + virtual ~ServerSideDecorationPaletteManagerInterface(); + /** + * Returns any existing palette for a given surface + * This returns a null pointer if no ServerSideDecorationPaletteInterface exists. + */ + ServerSideDecorationPaletteInterface* paletteForSurface(SurfaceInterface *); + +Q_SIGNALS: + /** + * Emitted whenever a new ServerSideDecorationPaletteInterface is created. + **/ + void paletteCreated(KWayland::Server::ServerSideDecorationPaletteInterface*); + +private: + explicit ServerSideDecorationPaletteManagerInterface(Display *display, QObject *parent = nullptr); + friend class Display; + class Private; + Private *d_func() const; +}; + +/** + * Provides the palette + * This interface is attached to a wl_surface and informs the server of a requested palette + * @since 5.42 + */ +class KWAYLANDSERVER_EXPORT ServerSideDecorationPaletteInterface : public Resource +{ + Q_OBJECT +public: + virtual ~ServerSideDecorationPaletteInterface(); + + /** + * @returns the palette or an empty string if unset + */ + QString palette() const; + + /** + * @returns The SurfaceInterface this ServerSideDecorationPaletteInterface references. + **/ + SurfaceInterface *surface() const; + +Q_SIGNALS: + /** + * Emitted when the palette changes or is first received + */ + void paletteChanged(const QString &palette); + +private: + explicit ServerSideDecorationPaletteInterface(ServerSideDecorationPaletteManagerInterface *parent, SurfaceInterface *s, wl_resource *parentResource); + friend class ServerSideDecorationPaletteManagerInterface; + + class Private; + Private *d_func() const; +}; + +} +} + +#endif diff --git a/src/server/surface_interface.cpp b/src/server/surface_interface.cpp index 37179a6..0e8936c 100644 --- a/src/server/surface_interface.cpp +++ b/src/server/surface_interface.cpp @@ -1,865 +1,888 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "surface_interface.h" #include "surface_interface_p.h" #include "buffer_interface.h" #include "clientconnection.h" #include "compositor_interface.h" +#include "idleinhibit_interface_p.h" #include "pointerconstraints_interface_p.h" #include "region_interface.h" #include "subcompositor_interface.h" #include "subsurface_interface_p.h" // Qt #include // Wayland #include // std #include namespace KWayland { namespace Server { SurfaceInterface::Private::Private(SurfaceInterface *q, CompositorInterface *c, wl_resource *parentResource) : Resource::Private(q, c, parentResource, &wl_surface_interface, &s_interface) { } SurfaceInterface::Private::~Private() { destroy(); } void SurfaceInterface::Private::addChild(QPointer< SubSurfaceInterface > child) { // protocol is not precise on how to handle the addition of new sub surfaces pending.children.append(child); subSurfacePending.children.append(child); current.children.append(child); Q_Q(SurfaceInterface); emit q->subSurfaceTreeChanged(); QObject::connect(child.data(), &SubSurfaceInterface::positionChanged, q, &SurfaceInterface::subSurfaceTreeChanged); QObject::connect(child->surface().data(), &SurfaceInterface::damaged, q, &SurfaceInterface::subSurfaceTreeChanged); QObject::connect(child->surface().data(), &SurfaceInterface::unmapped, q, &SurfaceInterface::subSurfaceTreeChanged); QObject::connect(child->surface().data(), &SurfaceInterface::subSurfaceTreeChanged, q, &SurfaceInterface::subSurfaceTreeChanged); } void SurfaceInterface::Private::removeChild(QPointer< SubSurfaceInterface > child) { // protocol is not precise on how to handle the addition of new sub surfaces pending.children.removeAll(child); subSurfacePending.children.removeAll(child); current.children.removeAll(child); Q_Q(SurfaceInterface); emit q->subSurfaceTreeChanged(); QObject::disconnect(child.data(), &SubSurfaceInterface::positionChanged, q, &SurfaceInterface::subSurfaceTreeChanged); if (!child->surface().isNull()) { QObject::disconnect(child->surface().data(), &SurfaceInterface::damaged, q, &SurfaceInterface::subSurfaceTreeChanged); QObject::disconnect(child->surface().data(), &SurfaceInterface::unmapped, q, &SurfaceInterface::subSurfaceTreeChanged); QObject::disconnect(child->surface().data(), &SurfaceInterface::subSurfaceTreeChanged, q, &SurfaceInterface::subSurfaceTreeChanged); } } bool SurfaceInterface::Private::raiseChild(QPointer subsurface, SurfaceInterface *sibling) { Q_Q(SurfaceInterface); auto it = std::find(pending.children.begin(), pending.children.end(), subsurface); if (it == pending.children.end()) { return false; } if (pending.children.count() == 1) { // nothing to do return true; } if (sibling == q) { // it's to the parent, so needs to become last item pending.children.append(*it); pending.children.erase(it); pending.childrenChanged = true; return true; } if (!sibling->subSurface()) { // not a sub surface return false; } auto siblingIt = std::find(pending.children.begin(), pending.children.end(), sibling->subSurface()); if (siblingIt == pending.children.end() || siblingIt == it) { // not a sibling return false; } auto value = (*it); pending.children.erase(it); // find the iterator again siblingIt = std::find(pending.children.begin(), pending.children.end(), sibling->subSurface()); pending.children.insert(++siblingIt, value); pending.childrenChanged = true; return true; } bool SurfaceInterface::Private::lowerChild(QPointer subsurface, SurfaceInterface *sibling) { Q_Q(SurfaceInterface); auto it = std::find(pending.children.begin(), pending.children.end(), subsurface); if (it == pending.children.end()) { return false; } if (pending.children.count() == 1) { // nothing to do return true; } if (sibling == q) { // it's to the parent, so needs to become first item auto value = *it; pending.children.erase(it); pending.children.prepend(value); pending.childrenChanged = true; return true; } if (!sibling->subSurface()) { // not a sub surface return false; } auto siblingIt = std::find(pending.children.begin(), pending.children.end(), sibling->subSurface()); if (siblingIt == pending.children.end() || siblingIt == it) { // not a sibling return false; } auto value = (*it); pending.children.erase(it); // find the iterator again siblingIt = std::find(pending.children.begin(), pending.children.end(), sibling->subSurface()); pending.children.insert(siblingIt, value); pending.childrenChanged = true; return true; } void SurfaceInterface::Private::setShadow(const QPointer &shadow) { pending.shadow = shadow; pending.shadowIsSet = true; } void SurfaceInterface::Private::setBlur(const QPointer &blur) { pending.blur = blur; pending.blurIsSet = true; } void SurfaceInterface::Private::setSlide(const QPointer &slide) { pending.slide = slide; pending.slideIsSet = true; } void SurfaceInterface::Private::setContrast(const QPointer &contrast) { pending.contrast = contrast; pending.contrastIsSet = true; } void SurfaceInterface::Private::installPointerConstraint(LockedPointerInterface *lock) { Q_ASSERT(lockedPointer.isNull()); Q_ASSERT(confinedPointer.isNull()); lockedPointer = QPointer(lock); if (lock->lifeTime() == LockedPointerInterface::LifeTime::OneShot) { constrainsOneShotConnection = QObject::connect(lock, &LockedPointerInterface::lockedChanged, q_func(), [this] { if (lockedPointer.isNull()) { return; } if (!lockedPointer->isLocked()) { lockedPointer.clear(); disconnect(constrainsOneShotConnection); constrainsOneShotConnection = QMetaObject::Connection(); disconnect(constrainsUnboundConnection); constrainsUnboundConnection = QMetaObject::Connection(); emit q_func()->pointerConstraintsChanged(); } } ); } constrainsUnboundConnection = QObject::connect(lock, &LockedPointerInterface::unbound, q_func(), [this] { if (lockedPointer.isNull()) { return; } lockedPointer.clear(); disconnect(constrainsOneShotConnection); constrainsOneShotConnection = QMetaObject::Connection(); disconnect(constrainsUnboundConnection); constrainsUnboundConnection = QMetaObject::Connection(); emit q_func()->pointerConstraintsChanged(); } ); emit q_func()->pointerConstraintsChanged(); } +void SurfaceInterface::Private::installIdleInhibitor(IdleInhibitorInterface *inhibitor) +{ + idleInhibitors << inhibitor; + QObject::connect(inhibitor, &IdleInhibitorInterface::aboutToBeUnbound, q, + [this, inhibitor] { + idleInhibitors.removeOne(inhibitor); + if (idleInhibitors.isEmpty()) { + emit q_func()->inhibitsIdleChanged(); + } + } + ); + if (idleInhibitors.count() == 1) { + emit q_func()->inhibitsIdleChanged(); + } +} + void SurfaceInterface::Private::installPointerConstraint(ConfinedPointerInterface *confinement) { Q_ASSERT(lockedPointer.isNull()); Q_ASSERT(confinedPointer.isNull()); confinedPointer = QPointer(confinement); if (confinement->lifeTime() == ConfinedPointerInterface::LifeTime::OneShot) { constrainsOneShotConnection = QObject::connect(confinement, &ConfinedPointerInterface::confinedChanged, q_func(), [this] { if (confinedPointer.isNull()) { return; } if (!confinedPointer->isConfined()) { confinedPointer.clear(); disconnect(constrainsOneShotConnection); constrainsOneShotConnection = QMetaObject::Connection(); disconnect(constrainsUnboundConnection); constrainsUnboundConnection = QMetaObject::Connection(); emit q_func()->pointerConstraintsChanged(); } } ); } constrainsUnboundConnection = QObject::connect(confinement, &ConfinedPointerInterface::unbound, q_func(), [this] { if (confinedPointer.isNull()) { return; } confinedPointer.clear(); disconnect(constrainsOneShotConnection); constrainsOneShotConnection = QMetaObject::Connection(); disconnect(constrainsUnboundConnection); constrainsUnboundConnection = QMetaObject::Connection(); emit q_func()->pointerConstraintsChanged(); } ); emit q_func()->pointerConstraintsChanged(); } #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct wl_surface_interface SurfaceInterface::Private::s_interface = { resourceDestroyedCallback, attachCallback, damageCallback, frameCallaback, opaqueRegionCallback, inputRegionCallback, commitCallback, bufferTransformCallback, bufferScaleCallback }; #endif SurfaceInterface::SurfaceInterface(CompositorInterface *parent, wl_resource *parentResource) : Resource(new Private(this, parent, parentResource)) { } SurfaceInterface::~SurfaceInterface() = default; void SurfaceInterface::frameRendered(quint32 msec) { Q_D(); // notify all callbacks const bool needsFlush = !d->current.callbacks.isEmpty(); while (!d->current.callbacks.isEmpty()) { wl_resource *r = d->current.callbacks.takeFirst(); wl_callback_send_done(r, msec); wl_resource_destroy(r); } for (auto it = d->current.children.constBegin(); it != d->current.children.constEnd(); ++it) { const auto &subSurface = *it; if (subSurface.isNull() || subSurface->d_func()->surface.isNull()) { continue; } subSurface->d_func()->surface->frameRendered(msec); } if (needsFlush) { client()->flush(); } } void SurfaceInterface::Private::destroy() { // copy all existing callbacks to new list and clear existing lists // the wl_resource_destroy on the callback resource goes into destroyFrameCallback // which would modify the list we are iterating on QList callbacksToDestroy; callbacksToDestroy << current.callbacks; current.callbacks.clear(); callbacksToDestroy << pending.callbacks; pending.callbacks.clear(); callbacksToDestroy << subSurfacePending.callbacks; subSurfacePending.callbacks.clear(); for (auto it = callbacksToDestroy.constBegin(), end = callbacksToDestroy.constEnd(); it != end; it++) { wl_resource_destroy(*it); } if (current.buffer) { current.buffer->unref(); } } void SurfaceInterface::Private::swapStates(State *source, State *target, bool emitChanged) { Q_Q(SurfaceInterface); bool bufferChanged = source->bufferIsSet; const bool opaqueRegionChanged = source->opaqueIsSet; const bool inputRegionChanged = source->inputIsSet; const bool scaleFactorChanged = source->scaleIsSet && (target->scale != source->scale); const bool transformChanged = source->transformIsSet && (target->transform != source->transform); const bool shadowChanged = source->shadowIsSet; const bool blurChanged = source->blurIsSet; const bool contrastChanged = source->contrastIsSet; const bool slideChanged = source->slideIsSet; const bool childrenChanged = source->childrenChanged; bool sizeChanged = false; auto buffer = target->buffer; if (bufferChanged) { // TODO: is the reffing correct for subsurfaces? QSize oldSize; if (target->buffer) { oldSize = target->buffer->size(); if (emitChanged) { target->buffer->unref(); QObject::disconnect(target->buffer, &BufferInterface::sizeChanged, q, &SurfaceInterface::sizeChanged); } else { delete target->buffer; target->buffer = nullptr; } } if (source->buffer) { if (emitChanged) { source->buffer->ref(); QObject::connect(source->buffer, &BufferInterface::sizeChanged, q, &SurfaceInterface::sizeChanged); } const QSize newSize = source->buffer->size(); sizeChanged = newSize.isValid() && newSize != oldSize; } if (!target->buffer && !source->buffer && emitChanged) { // null buffer set on a not mapped surface, don't emit unmapped bufferChanged = false; } buffer = source->buffer; } // copy values if (bufferChanged) { target->buffer = buffer; target->damage = source->damage; target->bufferIsSet = source->bufferIsSet; } if (childrenChanged) { target->childrenChanged = source->childrenChanged; target->children = source->children; } target->callbacks.append(source->callbacks); if (shadowChanged) { target->shadow = source->shadow; target->shadowIsSet = true; } if (blurChanged) { target->blur = source->blur; target->blurIsSet = true; } if (contrastChanged) { target->contrast = source->contrast; target->contrastIsSet = true; } if (slideChanged) { target->slide = source->slide; target->slideIsSet = true; } if (inputRegionChanged) { target->input = source->input; target->inputIsInfinite = source->inputIsInfinite; target->inputIsSet = true; } if (opaqueRegionChanged) { target->opaque = source->opaque; target->opaqueIsSet = true; } if (scaleFactorChanged) { target->scale = source->scale; target->scaleIsSet = true; } if (transformChanged) { target->transform = source->transform; target->transformIsSet = true; } if (!lockedPointer.isNull()) { lockedPointer->d_func()->commit(); } if (!confinedPointer.isNull()) { confinedPointer->d_func()->commit(); } *source = State{}; source->children = target->children; if (opaqueRegionChanged) { emit q->opaqueChanged(target->opaque); } if (inputRegionChanged) { emit q->inputChanged(target->input); } if (scaleFactorChanged) { emit q->scaleChanged(target->scale); if (buffer && !sizeChanged) { emit q->sizeChanged(); } } if (transformChanged) { emit q->transformChanged(target->transform); } if (bufferChanged && emitChanged) { if (!target->damage.isEmpty()) { const QRegion windowRegion = QRegion(0, 0, q->size().width(), q->size().height()); if (!windowRegion.isEmpty()) { target->damage = windowRegion.intersected(target->damage); if (emitChanged) { subSurfaceIsMapped = true; trackedDamage = trackedDamage.united(target->damage); emit q->damaged(target->damage); // workaround for https://bugreports.qt.io/browse/QTBUG-52092 // if the surface is a sub-surface, but the main surface is not yet mapped, fake frame rendered if (subSurface && !subSurface->mainSurface()->buffer()) { q->frameRendered(0); } } } } else if (!target->buffer && emitChanged) { subSurfaceIsMapped = false; emit q->unmapped(); } } if (!emitChanged) { return; } if (sizeChanged) { emit q->sizeChanged(); } if (shadowChanged) { emit q->shadowChanged(); } if (blurChanged) { emit q->blurChanged(); } if (contrastChanged) { emit q->contrastChanged(); } if (slideChanged) { emit q->slideOnShowHideChanged(); } if (childrenChanged) { emit q->subSurfaceTreeChanged(); } } void SurfaceInterface::Private::commit() { if (!subSurface.isNull() && subSurface->isSynchronized()) { swapStates(&pending, &subSurfacePending, false); } else { swapStates(&pending, ¤t, true); if (!subSurface.isNull()) { subSurface->d_func()->commit(); } // commit all subSurfaces to apply position changes // "The cached state is applied to the sub-surface immediately after the parent surface's state is applied" for (auto it = current.children.constBegin(); it != current.children.constEnd(); ++it) { const auto &subSurface = *it; if (subSurface.isNull()) { continue; } subSurface->d_func()->commit(); } } } void SurfaceInterface::Private::commitSubSurface() { if (subSurface.isNull() || !subSurface->isSynchronized()) { return; } swapStates(&subSurfacePending, ¤t, true); // "The cached state is applied to the sub-surface immediately after the parent surface's state is applied" for (auto it = current.children.constBegin(); it != current.children.constEnd(); ++it) { const auto &subSurface = *it; if (subSurface.isNull() || !subSurface->isSynchronized()) { continue; } subSurface->d_func()->commit(); } } void SurfaceInterface::Private::damage(const QRect &rect) { if (!pending.bufferIsSet || (pending.bufferIsSet && !pending.buffer)) { // TODO: should we send an error? return; } pending.damage = pending.damage.united(rect); } void SurfaceInterface::Private::setScale(qint32 scale) { pending.scale = scale; pending.scaleIsSet = true; } void SurfaceInterface::Private::setTransform(OutputInterface::Transform transform) { pending.transform = transform; } void SurfaceInterface::Private::addFrameCallback(uint32_t callback) { wl_resource *r = client->createResource(&wl_callback_interface, 1, callback); if (!r) { wl_resource_post_no_memory(resource); return; } wl_resource_set_implementation(r, nullptr, this, destroyFrameCallback); pending.callbacks << r; } void SurfaceInterface::Private::attachBuffer(wl_resource *buffer, const QPoint &offset) { pending.bufferIsSet = true; pending.offset = offset; if (pending.buffer) { delete pending.buffer; } if (!buffer) { // got a null buffer, deletes content in next frame pending.buffer = nullptr; pending.damage = QRegion(); return; } Q_Q(SurfaceInterface); pending.buffer = new BufferInterface(buffer, q); QObject::connect(pending.buffer, &BufferInterface::aboutToBeDestroyed, q, [this](BufferInterface *buffer) { if (pending.buffer == buffer) { pending.buffer = nullptr; } if (subSurfacePending.buffer == buffer) { subSurfacePending.buffer = nullptr; } if (current.buffer == buffer) { current.buffer->unref(); current.buffer = nullptr; } } ); } void SurfaceInterface::Private::destroyFrameCallback(wl_resource *r) { auto s = cast(r); s->current.callbacks.removeAll(r); s->pending.callbacks.removeAll(r); s->subSurfacePending.callbacks.removeAll(r); } void SurfaceInterface::Private::attachCallback(wl_client *client, wl_resource *resource, wl_resource *buffer, int32_t sx, int32_t sy) { Q_UNUSED(client) cast(resource)->attachBuffer(buffer, QPoint(sx, sy)); } void SurfaceInterface::Private::damageCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { Q_UNUSED(client) cast(resource)->damage(QRect(x, y, width, height)); } void SurfaceInterface::Private::frameCallaback(wl_client *client, wl_resource *resource, uint32_t callback) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->addFrameCallback(callback); } void SurfaceInterface::Private::opaqueRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region) { auto s = cast(resource); Q_ASSERT(client == *s->client); auto r = RegionInterface::get(region); s->setOpaque(r ? r->region() : QRegion()); } void SurfaceInterface::Private::setOpaque(const QRegion ®ion) { pending.opaqueIsSet = true; pending.opaque = region; } void SurfaceInterface::Private::inputRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region) { auto s = cast(resource); Q_ASSERT(client == *s->client); auto r = RegionInterface::get(region); s->setInput(r ? r->region() : QRegion(), !r); } void SurfaceInterface::Private::setInput(const QRegion ®ion, bool isInfinite) { pending.inputIsSet = true; pending.inputIsInfinite = isInfinite; pending.input = region; } void SurfaceInterface::Private::commitCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) cast(resource)->commit(); } void SurfaceInterface::Private::bufferTransformCallback(wl_client *client, wl_resource *resource, int32_t transform) { Q_UNUSED(client) cast(resource)->setTransform(OutputInterface::Transform(transform)); } void SurfaceInterface::Private::bufferScaleCallback(wl_client *client, wl_resource *resource, int32_t scale) { Q_UNUSED(client) cast(resource)->setScale(scale); } QRegion SurfaceInterface::damage() const { Q_D(); return d->current.damage; } QRegion SurfaceInterface::opaque() const { Q_D(); return d->current.opaque; } QRegion SurfaceInterface::input() const { Q_D(); return d->current.input; } bool SurfaceInterface::inputIsInfitine() const { return inputIsInfinite(); } bool SurfaceInterface::inputIsInfinite() const { Q_D(); return d->current.inputIsInfinite; } qint32 SurfaceInterface::scale() const { Q_D(); return d->current.scale; } OutputInterface::Transform SurfaceInterface::transform() const { Q_D(); return d->current.transform; } BufferInterface *SurfaceInterface::buffer() { Q_D(); return d->current.buffer; } QPoint SurfaceInterface::offset() const { Q_D(); return d->current.offset; } SurfaceInterface *SurfaceInterface::get(wl_resource *native) { return Private::get(native); } SurfaceInterface *SurfaceInterface::get(quint32 id, const ClientConnection *client) { return Private::get(id, client); } QList< QPointer< SubSurfaceInterface > > SurfaceInterface::childSubSurfaces() const { Q_D(); return d->current.children; } QPointer< SubSurfaceInterface > SurfaceInterface::subSurface() const { Q_D(); return d->subSurface; } QSize SurfaceInterface::size() const { Q_D(); // TODO: apply transform to the buffer size if (d->current.buffer) { return d->current.buffer->size() / scale(); } return QSize(); } QPointer< ShadowInterface > SurfaceInterface::shadow() const { Q_D(); return d->current.shadow; } QPointer< BlurInterface > SurfaceInterface::blur() const { Q_D(); return d->current.blur; } QPointer< ContrastInterface > SurfaceInterface::contrast() const { Q_D(); return d->current.contrast; } QPointer< SlideInterface > SurfaceInterface::slideOnShowHide() const { Q_D(); return d->current.slide; } bool SurfaceInterface::isMapped() const { Q_D(); if (d->subSurface) { // from spec: // "A sub-surface becomes mapped, when a non-NULL wl_buffer is applied and the parent surface is mapped." return d->subSurfaceIsMapped && !d->subSurface->parentSurface().isNull() && d->subSurface->parentSurface()->isMapped(); } return d->current.buffer != nullptr; } QRegion SurfaceInterface::trackedDamage() const { Q_D(); return d->trackedDamage; } void SurfaceInterface::resetTrackedDamage() { Q_D(); d->trackedDamage = QRegion(); } QVector SurfaceInterface::outputs() const { Q_D(); return d->outputs; } void SurfaceInterface::setOutputs(const QVector &outputs) { Q_D(); QVector removedOutputs = d->outputs; for (auto it = outputs.constBegin(), end = outputs.constEnd(); it != end; ++it) { const auto o = *it; removedOutputs.removeOne(o); } for (auto it = removedOutputs.constBegin(), end = removedOutputs.constEnd(); it != end; ++it) { const auto resources = (*it)->clientResources(client()); for (wl_resource *r : resources) { wl_surface_send_leave(d->resource, r); } disconnect(d->outputDestroyedConnections.take(*it)); } QVector addedOutputsOutputs = outputs; for (auto it = d->outputs.constBegin(), end = d->outputs.constEnd(); it != end; ++it) { const auto o = *it; addedOutputsOutputs.removeOne(o); } for (auto it = addedOutputsOutputs.constBegin(), end = addedOutputsOutputs.constEnd(); it != end; ++it) { const auto o = *it; const auto resources = o->clientResources(client()); for (wl_resource *r : resources) { wl_surface_send_enter(d->resource, r); } d->outputDestroyedConnections[o] = connect(o, &Global::aboutToDestroyGlobal, this, [this, o] { Q_D(); auto outputs = d->outputs; if (outputs.removeOne(o)) { setOutputs(outputs); }}); } // TODO: send enter when the client binds the OutputInterface another time d->outputs = outputs; } SurfaceInterface *SurfaceInterface::surfaceAt(const QPointF &position) { if (!isMapped()) { return nullptr; } Q_D(); // go from top to bottom. Top most child is last in list QListIterator> it(d->current.children); it.toBack(); while (it.hasPrevious()) { const auto ¤t = it.previous(); auto surface = current->surface(); if (surface.isNull()) { continue; } if (auto s = surface->surfaceAt(position - current->position())) { return s; } } // check whether the geometry contains the pos if (!size().isEmpty() && QRectF(QPoint(0, 0), size()).contains(position)) { return this; } return nullptr; } QPointer SurfaceInterface::lockedPointer() const { Q_D(); return d->lockedPointer; } QPointer SurfaceInterface::confinedPointer() const { Q_D(); return d->confinedPointer; } +bool SurfaceInterface::inhibitsIdle() const +{ + Q_D(); + return !d->idleInhibitors.isEmpty(); +} + SurfaceInterface::Private *SurfaceInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/surface_interface.h b/src/server/surface_interface.h index c7f2fc6..232c38c 100644 --- a/src/server/surface_interface.h +++ b/src/server/surface_interface.h @@ -1,330 +1,346 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #ifndef WAYLAND_SERVER_SURFACE_INTERFACE_H #define WAYLAND_SERVER_SURFACE_INTERFACE_H #include "resource.h" #include "output_interface.h" #include #include #include #include namespace KWayland { namespace Server { class BlurManagerInterface; class BlurInterface; class BufferInterface; class ConfinedPointerInterface; class ContrastInterface; class ContrastManagerInterface; class CompositorInterface; +class IdleInhibitManagerUnstableV1Interface; class LockedPointerInterface; class PointerConstraintsUnstableV1Interface; class ShadowManagerInterface; class ShadowInterface; class SlideInterface; class SubSurfaceInterface; /** * @brief Resource representing a wl_surface. * * The SurfaceInterface gets created by the CompositorInterface. A SurfaceInterface normally * takes up a role by being "attached" to either a ShellSurfaceInterface, a SubSurfaceInterface * or a Cursor. * * The implementation of the SurfaceInterface does not only wrap the features exposed by wl_surface, * but goes further by integrating the information added to a SurfaceInterface by other interfaces. * This should make interacting from the server easier, it only needs to monitor the SurfaceInterface * and does not need to track each specific interface. * * The SurfaceInterface takes care of reference/unreferencing the BufferInterface attached to it. * As long as a BufferInterface is attached, the released signal won't be sent. If the BufferInterface * is no longer needed by the SurfaceInterface, it will get unreferenced and might be automatically * deleted (if it's no longer referenced). * * @see CompositorInterface * @see BufferInterface * @see SubSurfaceInterface * @see BlurInterface * @see ContrastInterface * @see ShadowInterface * @see SlideInterface **/ class KWAYLANDSERVER_EXPORT SurfaceInterface : public Resource { Q_OBJECT /** * The current damage region. **/ Q_PROPERTY(QRegion damage READ damage NOTIFY damaged) /** * The opaque region for a translucent buffer. **/ Q_PROPERTY(QRegion opaque READ opaque NOTIFY opaqueChanged) /** * The current input region. **/ Q_PROPERTY(QRegion input READ input NOTIFY inputChanged) Q_PROPERTY(qint32 scale READ scale NOTIFY scaleChanged) Q_PROPERTY(KWayland::Server::OutputInterface::Transform transform READ transform NOTIFY transformChanged) Q_PROPERTY(QSize size READ size NOTIFY sizeChanged) public: virtual ~SurfaceInterface(); void frameRendered(quint32 msec); QRegion damage() const; QRegion opaque() const; QRegion input() const; /** * Use Surface::inputIsInfinite instead. * @deprecated * @see inputIsInfinite */ bool inputIsInfitine() const; /** * Replaces Surface::inputIsInfitine instead. * @since 5.5 */ bool inputIsInfinite() const; qint32 scale() const; OutputInterface::Transform transform() const; /** * @returns the current BufferInterface, might be @c nullptr. **/ BufferInterface *buffer(); QPoint offset() const; /** * The size of the Surface in global compositor space. * @see For buffer size use BufferInterface::size * from SurfaceInterface::buffer * @since 5.3 **/ QSize size() const; /** * @returns The SubSurface for this Surface in case there is one. **/ QPointer subSurface() const; /** * @returns Children in stacking order from bottom (first) to top (last). **/ QList> childSubSurfaces() const; /** * @returns The Shadow for this Surface. * @since 5.4 **/ QPointer shadow() const; /** * @returns The Blur for this Surface. * @since 5.5 **/ QPointer blur() const; /** * @returns The Slide for this Surface. * @since 5.5 **/ QPointer slideOnShowHide() const; /** * @returns The Contrast for this Surface. * @since 5.5 **/ QPointer contrast() const; /** * Whether the SurfaceInterface is currently considered to be mapped. * A SurfaceInterface is mapped if it has a non-null BufferInterface attached. * If the SurfaceInterface references a SubSurfaceInterface it is only considered * mapped if it has a BufferInterface attached and the parent SurfaceInterface is mapped. * * @returns Whether the SurfaceInterface is currently mapped * @since 5.22 **/ bool isMapped() const; /** * Returns the tracked damage since the last call to {@link resetTrackedDamage}. * In contrast to {@link damage} this method does not reset the damage when * a new BufferInterface gets committed. This allows a compositor to properly * track the damage over multiple commits even if it didn't render each new * BufferInterface. * * The damage gets reset whenever {@link resetTrackedDamage} is called. * This allows a compositor to properly track the change in its rendering scene * for this SurfaceInterface. After it updates its internal state (e.g. by creating * an OpenGL texture from the BufferInterface) it can invoke {@link resetTrackedDamage} * and the damage tracker will start to track further damage changes. * * @returns Combined damage since last call to resetTrackedDamage * @see damage * @see resetTrackedDamage * @since 5.22 **/ QRegion trackedDamage() const; /** * Reset the damage tracking. The compositor should invoke this method once it updated * it's internal state and processed the current damage. * @see trackedDamage * @since 5.22 **/ void resetTrackedDamage(); /** * Finds the SurfaceInterface at the given @p position in surface-local coordinates. * This can be either a descendant SurfaceInterface honoring the stacking order or * the SurfaceInterface itself if its geometry contains the given @p position. * * If no such SurfaceInterface is found, e.g. because the SurfaceInterface is unmapped, * @c nullptr is returned. * * @param position The position in surface-local coordinates * @returns Child surface at the given @p position or surface itself at the position, might be @c nullptr * @since 5.22 **/ SurfaceInterface *surfaceAt(const QPointF &position); /** * Sets the @p outputs this SurfaceInterface overlaps with, may be empty. * * The compositor should update whenever the SurfaceInterface becomes visible on * an OutputInterface by e.g. getting (un)mapped, resized, moved, etc. * * @see outputs * @since 5.27 **/ void setOutputs(const QVector &outputs); /** * @returns All OutputInterfaces the SurfaceInterface is on. * @see setOutputs * @since 5.27 **/ QVector outputs() const; /** * Pointer confinement installed on this SurfaceInterface. * @see pointerConstraintsChanged * @since 5.29 **/ QPointer confinedPointer() const; /** * Pointer lock installed on this SurfaceInterface. * @see pointerConstraintsChanged * @since 5.29 **/ QPointer lockedPointer() const; + /** + * @returns Whether this SurfaceInterface wants idle to be inhibited on the Output it is shown + * @see inhibitsIdleChanged + * @since 5.41 + **/ + bool inhibitsIdle() const; + /** * @returns The SurfaceInterface for the @p native resource. **/ static SurfaceInterface *get(wl_resource *native); /** * @returns The SurfaceInterface with given @p id for @p client, if it exists, otherwise @c nullptr. * @since 5.3 **/ static SurfaceInterface *get(quint32 id, const ClientConnection *client); Q_SIGNALS: /** * Emitted whenever the SurfaceInterface got damaged. * The signal is only emitted during the commit of state. * A damage means that a new BufferInterface got attached. * * @see buffer * @see damage **/ void damaged(const QRegion&); void opaqueChanged(const QRegion&); void inputChanged(const QRegion&); void scaleChanged(qint32); void transformChanged(KWayland::Server::OutputInterface::Transform); /** * Emitted when the Surface removes its content **/ void unmapped(); /** * @since 5.3 **/ void sizeChanged(); /** * @since 5.4 **/ void shadowChanged(); /** * @since 5.5 **/ void blurChanged(); /** * @since 5.5 **/ void slideOnShowHideChanged(); /** * @since 5.5 **/ void contrastChanged(); /** * Emitted whenever the tree of sub-surfaces changes in a way which requires a repaint. * @since 5.22 **/ void subSurfaceTreeChanged(); /** * Emitted whenever a pointer constraint get (un)installed on this SurfaceInterface. * * The pointer constraint does not get activated, the compositor needs to activate * the lock/confinement. * * @see confinedPointer * @see lockedPointer * @since 5.29 **/ void pointerConstraintsChanged(); + /** + * Emitted whenever the SurfaceInterface starts/ends to inhibit idle. + * @see inhibitsIdle + * @since 5.41 + **/ + void inhibitsIdleChanged(); + private: friend class CompositorInterface; friend class SubSurfaceInterface; friend class ShadowManagerInterface; friend class BlurManagerInterface; friend class SlideManagerInterface; friend class ContrastManagerInterface; + friend class IdleInhibitManagerUnstableV1Interface; friend class PointerConstraintsUnstableV1Interface; explicit SurfaceInterface(CompositorInterface *parent, wl_resource *parentResource); class Private; Private *d_func() const; }; } } Q_DECLARE_METATYPE(KWayland::Server::SurfaceInterface*) #endif diff --git a/src/server/surface_interface_p.h b/src/server/surface_interface_p.h index 30c39db..974fe3b 100644 --- a/src/server/surface_interface_p.h +++ b/src/server/surface_interface_p.h @@ -1,137 +1,141 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #ifndef WAYLAND_SERVER_SURFACE_INTERFACE_P_H #define WAYLAND_SERVER_SURFACE_INTERFACE_P_H #include "surface_interface.h" #include "resource_p.h" // Qt #include // Wayland #include namespace KWayland { namespace Server { +class IdleInhibitorInterface; + class SurfaceInterface::Private : public Resource::Private { public: struct State { QRegion damage = QRegion(); QRegion opaque = QRegion(); QRegion input = QRegion(); bool inputIsSet = false; bool opaqueIsSet = false; bool bufferIsSet = false; bool shadowIsSet = false; bool blurIsSet = false; bool contrastIsSet = false; bool slideIsSet = false; bool inputIsInfinite = true; bool childrenChanged = false; bool scaleIsSet = false; bool transformIsSet = false; qint32 scale = 1; OutputInterface::Transform transform = OutputInterface::Transform::Normal; QList callbacks = QList(); QPoint offset = QPoint(); BufferInterface *buffer = nullptr; // stacking order: bottom (first) -> top (last) QList> children; QPointer shadow; QPointer blur; QPointer contrast; QPointer slide; }; Private(SurfaceInterface *q, CompositorInterface *c, wl_resource *parentResource); ~Private(); void destroy(); void addChild(QPointer subsurface); void removeChild(QPointer subsurface); bool raiseChild(QPointer subsurface, SurfaceInterface *sibling); bool lowerChild(QPointer subsurface, SurfaceInterface *sibling); void setShadow(const QPointer &shadow); void setBlur(const QPointer &blur); void setContrast(const QPointer &contrast); void setSlide(const QPointer &slide); void installPointerConstraint(LockedPointerInterface *lock); void installPointerConstraint(ConfinedPointerInterface *confinement); + void installIdleInhibitor(IdleInhibitorInterface *inhibitor); void commitSubSurface(); void commit(); State current; State pending; State subSurfacePending; QPointer subSurface; QRegion trackedDamage; // workaround for https://bugreports.qt.io/browse/QTBUG-52192 // A subsurface needs to be considered mapped even if it doesn't have a buffer attached // Otherwise Qt's sub-surfaces will never be visible and the client will freeze due to // waiting on the frame callback of the never visible surface bool subSurfaceIsMapped = true; QVector outputs; QPointer lockedPointer; QPointer confinedPointer; QHash outputDestroyedConnections; + QVector idleInhibitors; private: QMetaObject::Connection constrainsOneShotConnection; QMetaObject::Connection constrainsUnboundConnection; SurfaceInterface *q_func() { return reinterpret_cast(q); } void swapStates(State *source, State *target, bool emitChanged); void damage(const QRect &rect); void setScale(qint32 scale); void setTransform(OutputInterface::Transform transform); void addFrameCallback(uint32_t callback); void attachBuffer(wl_resource *buffer, const QPoint &offset); void setOpaque(const QRegion ®ion); void setInput(const QRegion ®ion, bool isInfinite); static void destroyFrameCallback(wl_resource *r); static void attachCallback(wl_client *client, wl_resource *resource, wl_resource *buffer, int32_t sx, int32_t sy); static void damageCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height); static void frameCallaback(wl_client *client, wl_resource *resource, uint32_t callback); static void opaqueRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region); static void inputRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region); static void commitCallback(wl_client *client, wl_resource *resource); // since version 2 static void bufferTransformCallback(wl_client *client, wl_resource *resource, int32_t transform); // since version 3 static void bufferScaleCallback(wl_client *client, wl_resource *resource, int32_t scale); static const struct wl_surface_interface s_interface; }; } } #endif diff --git a/src/server/xdgforeign_v2_interface.cpp b/src/server/xdgforeign_v2_interface.cpp index ca79976..645ebb7 100644 --- a/src/server/xdgforeign_v2_interface.cpp +++ b/src/server/xdgforeign_v2_interface.cpp @@ -1,459 +1,462 @@ /**************************************************************************** Copyright 2017 Marco Martin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . ****************************************************************************/ #include "xdgforeign_interface.h" #include "xdgforeign_v2_interface_p.h" #include "display.h" #include "global_p.h" #include "resource_p.h" #include "surface_interface_p.h" #include "wayland-xdg-foreign-unstable-v2-server-protocol.h" #include #include namespace KWayland { namespace Server { class Q_DECL_HIDDEN XdgExporterUnstableV2Interface::Private : public Global::Private { public: Private(XdgExporterUnstableV2Interface *q, Display *d, XdgForeignInterface *foreignInterface); XdgForeignInterface *foreignInterface; QHash exportedSurfaces; private: void bind(wl_client *client, uint32_t version, uint32_t id) override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static void destroyCallback(wl_client *client, wl_resource *resource); static void exportCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface); XdgExporterUnstableV2Interface *q; static const struct zxdg_exporter_v2_interface s_interface; static const quint32 s_version; }; const quint32 XdgExporterUnstableV2Interface::Private::s_version = 1; #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct zxdg_exporter_v2_interface XdgExporterUnstableV2Interface::Private::s_interface = { destroyCallback, exportCallback }; #endif XdgExporterUnstableV2Interface::XdgExporterUnstableV2Interface(Display *display, XdgForeignInterface *parent) : Global(new Private(this, display, parent), parent) { } XdgExporterUnstableV2Interface::~XdgExporterUnstableV2Interface() {} XdgExporterUnstableV2Interface::Private *XdgExporterUnstableV2Interface::d_func() const { return reinterpret_cast(d.data()); } XdgExportedUnstableV2Interface *XdgExporterUnstableV2Interface::exportedSurface(const QString &handle) { Q_D(); auto it = d->exportedSurfaces.constFind(handle); if (it != d->exportedSurfaces.constEnd()) { return it.value(); } return nullptr; } void XdgExporterUnstableV2Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) } void XdgExporterUnstableV2Interface::Private::exportCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface) { auto s = cast(resource); QPointer e = new XdgExportedUnstableV2Interface(s->q, surface); e->create(s->display->getConnection(client), wl_resource_get_version(resource), id); if (!e->resource()) { wl_resource_post_no_memory(resource); delete e; return; } const QString handle = QUuid::createUuid().toString(); //a surface not exported anymore connect(e.data(), &XdgExportedUnstableV2Interface::unbound, s->q, [s, handle]() { s->exportedSurfaces.remove(handle); emit s->q->surfaceUnexported(handle); }); //if the surface dies before this, this dies too connect(SurfaceInterface::get(surface), &Resource::unbound, s->q, [s, e, handle]() { if (e) { e->deleteLater(); } s->exportedSurfaces.remove(handle); emit s->q->surfaceUnexported(handle); }); s->exportedSurfaces[handle] = e; zxdg_exported_v2_send_handle(e->resource(), handle.toUtf8().constData()); emit s->q->surfaceExported(handle, e); } XdgExporterUnstableV2Interface::Private::Private(XdgExporterUnstableV2Interface *q, Display *d,XdgForeignInterface *foreignInterface) : Global::Private(d, &zxdg_exporter_v2_interface, s_version) , foreignInterface(foreignInterface) , q(q) { } void XdgExporterUnstableV2Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&zxdg_exporter_v2_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); // TODO: should we track? } void XdgExporterUnstableV2Interface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) // TODO: implement? } class Q_DECL_HIDDEN XdgImporterUnstableV2Interface::Private : public Global::Private { public: Private(XdgImporterUnstableV2Interface *q, Display *d, XdgForeignInterface *foreignInterface); XdgForeignInterface *foreignInterface; QHash importedSurfaces; //child->parent hash QHash parents; //parent->child hash QHash children; private: void bind(wl_client *client, uint32_t version, uint32_t id) override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static void destroyCallback(wl_client *client, wl_resource *resource); static void importCallback(wl_client *client, wl_resource *resource, uint32_t id, const char * handle); XdgImporterUnstableV2Interface *q; static const struct zxdg_importer_v2_interface s_interface; static const quint32 s_version; }; const quint32 XdgImporterUnstableV2Interface::Private::s_version = 1; #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct zxdg_importer_v2_interface XdgImporterUnstableV2Interface::Private::s_interface = { destroyCallback, importCallback }; #endif XdgImporterUnstableV2Interface::XdgImporterUnstableV2Interface(Display *display, XdgForeignInterface *parent) : Global(new Private(this, display, parent), parent) { } XdgImporterUnstableV2Interface::~XdgImporterUnstableV2Interface() { } XdgImportedUnstableV2Interface *XdgImporterUnstableV2Interface::importedSurface(const QString &handle) { Q_D(); auto it = d->importedSurfaces.constFind(handle); if (it != d->importedSurfaces.constEnd()) { return it.value(); } return nullptr; } SurfaceInterface *XdgImporterUnstableV2Interface::transientFor(SurfaceInterface *surface) { Q_D(); auto it = d->parents.constFind(surface); if (it == d->parents.constEnd()) { return nullptr; } return SurfaceInterface::get((*it)->parentResource()); } XdgImporterUnstableV2Interface::Private *XdgImporterUnstableV2Interface::d_func() const { return reinterpret_cast(d.data()); } void XdgImporterUnstableV2Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) } -void XdgImporterUnstableV2Interface::Private::importCallback(wl_client *client, wl_resource *resource, uint32_t id, const char * handle) +void XdgImporterUnstableV2Interface::Private::importCallback(wl_client *client, wl_resource *resource, uint32_t id, const char *h) { auto s = cast(resource); Q_ASSERT(s->foreignInterface); + const QString handle = QString::fromUtf8(h); - XdgExportedUnstableV2Interface *exp = s->foreignInterface->d->exporter->exportedSurface(QString::fromUtf8(handle)); + XdgExportedUnstableV2Interface *exp = s->foreignInterface->d->exporter->exportedSurface(handle); if (!exp) { zxdg_imported_v2_send_destroyed(resource); return; } wl_resource *surface = exp->parentResource(); if (!surface) { zxdg_imported_v2_send_destroyed(resource); return; } QPointer imp = new XdgImportedUnstableV2Interface(s->q, surface); imp->create(s->display->getConnection(client), wl_resource_get_version(resource), id); //surface no longer exported connect(exp, &XdgExportedUnstableV2Interface::unbound, s->q, [s, imp, handle]() { //imp valid when the exported is deleted before the imported if (imp) { zxdg_imported_v2_send_destroyed(imp->resource()); imp->deleteLater(); } - s->importedSurfaces.remove(QString::fromUtf8(handle)); - emit s->q->surfaceUnimported(QString::fromUtf8(handle)); + s->importedSurfaces.remove(handle); + emit s->q->surfaceUnimported(handle); }); connect(imp.data(), &XdgImportedUnstableV2Interface::childChanged, s->q, [s, imp](SurfaceInterface *child) { //remove any previous association auto it = s->children.find(imp); if (it != s->children.end()) { s->parents.remove(*it); s->children.erase(it); } s->parents[child] = imp; s->children[imp] = child; SurfaceInterface *parent = SurfaceInterface::get(imp->parentResource()); emit s->q->transientChanged(child, parent); //child surface destroyed connect(child, &Resource::unbound, s->q, [s, child]() { auto it = s->parents.find(child); if (it != s->parents.end()) { + KWayland::Server::XdgImportedUnstableV2Interface* parent = *it; s->children.remove(*it); s->parents.erase(it); - emit s->q->transientChanged(nullptr, SurfaceInterface::get((*it)->parentResource())); + emit s->q->transientChanged(nullptr, SurfaceInterface::get(parent->parentResource())); } }); }); //surface no longer imported connect(imp.data(), &XdgImportedUnstableV2Interface::unbound, s->q, [s, handle, imp]() { - s->importedSurfaces.remove(QString::fromUtf8(handle)); - emit s->q->surfaceUnimported(QString::fromUtf8(handle)); + s->importedSurfaces.remove(handle); + emit s->q->surfaceUnimported(handle); auto it = s->children.find(imp); if (it != s->children.end()) { + KWayland::Server::SurfaceInterface* child = *it; s->parents.remove(*it); s->children.erase(it); - emit s->q->transientChanged(*it, nullptr); + emit s->q->transientChanged(child, nullptr); } }); if (!imp->resource()) { wl_resource_post_no_memory(resource); delete imp; return; } - s->importedSurfaces[QString::fromUtf8(handle)] = imp; - emit s->q->surfaceImported(QString::fromUtf8(handle), imp); + s->importedSurfaces[handle] = imp; + emit s->q->surfaceImported(handle, imp); } XdgImporterUnstableV2Interface::Private::Private(XdgImporterUnstableV2Interface *q, Display *d, XdgForeignInterface *foreignInterface) : Global::Private(d, &zxdg_importer_v2_interface, s_version) , foreignInterface(foreignInterface) , q(q) { } void XdgImporterUnstableV2Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&zxdg_importer_v2_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); // TODO: should we track? } void XdgImporterUnstableV2Interface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) // TODO: implement? } class Q_DECL_HIDDEN XdgExportedUnstableV2Interface::Private : public Resource::Private { public: Private(XdgExportedUnstableV2Interface *q, XdgExporterUnstableV2Interface *c, wl_resource *parentResource); ~Private(); private: XdgExportedUnstableV2Interface *q_func() { return reinterpret_cast(q); } static const struct zxdg_exported_v2_interface s_interface; }; #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct zxdg_exported_v2_interface XdgExportedUnstableV2Interface::Private::s_interface = { resourceDestroyedCallback }; #endif XdgExportedUnstableV2Interface::XdgExportedUnstableV2Interface(XdgExporterUnstableV2Interface *parent, wl_resource *parentResource) : Resource(new Private(this, parent, parentResource)) { } XdgExportedUnstableV2Interface::~XdgExportedUnstableV2Interface() {} XdgExportedUnstableV2Interface::Private *XdgExportedUnstableV2Interface::d_func() const { return reinterpret_cast(d.data()); } XdgExportedUnstableV2Interface::Private::Private(XdgExportedUnstableV2Interface *q, XdgExporterUnstableV2Interface *c, wl_resource *parentResource) : Resource::Private(q, c, parentResource, &zxdg_exported_v2_interface, &s_interface) { } XdgExportedUnstableV2Interface::Private::~Private() {} class Q_DECL_HIDDEN XdgImportedUnstableV2Interface::Private : public Resource::Private { public: Private(XdgImportedUnstableV2Interface *q, XdgImporterUnstableV2Interface *c, wl_resource *parentResource); ~Private(); QPointer parentOf; private: static void setParentOfCallback(wl_client *client, wl_resource *resource, wl_resource * surface); XdgImportedUnstableV2Interface *q_func() { return reinterpret_cast(q); } static const struct zxdg_imported_v2_interface s_interface; }; #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct zxdg_imported_v2_interface XdgImportedUnstableV2Interface::Private::s_interface = { resourceDestroyedCallback, setParentOfCallback }; #endif XdgImportedUnstableV2Interface::XdgImportedUnstableV2Interface(XdgImporterUnstableV2Interface *parent, wl_resource *parentResource) : Resource(new Private(this, parent, parentResource)) { } XdgImportedUnstableV2Interface::~XdgImportedUnstableV2Interface() {} XdgImportedUnstableV2Interface::Private *XdgImportedUnstableV2Interface::d_func() const { return reinterpret_cast(d.data()); } SurfaceInterface *XdgImportedUnstableV2Interface::child() const { Q_D(); return d->parentOf.data(); } void XdgImportedUnstableV2Interface::Private::setParentOfCallback(wl_client *client, wl_resource *resource, wl_resource * surface) { auto s = cast(resource); SurfaceInterface *surf = SurfaceInterface::get(surface); if (!surf) { return; } s->parentOf = surf; emit s->q_func()->childChanged(surf); } XdgImportedUnstableV2Interface::Private::Private(XdgImportedUnstableV2Interface *q, XdgImporterUnstableV2Interface *c, wl_resource *parentResource) : Resource::Private(q, c, parentResource, &zxdg_imported_v2_interface, &s_interface) { } XdgImportedUnstableV2Interface::Private::~Private() {} } } diff --git a/src/tools/mapping.txt b/src/tools/mapping.txt index f2a2e97..20629a3 100644 --- a/src/tools/mapping.txt +++ b/src/tools/mapping.txt @@ -1,62 +1,64 @@ #wl_name;KWayland::ClientName wl_buffer;Buffer wl_compositor;Compositor wl_data_device;DataDevice wl_data_device_manager;DataDeviceManager wl_data_offer;DataOffer wl_data_source;DataSource wl_event_queue;EventQueue wl_keyboard;Keyboard wl_output;Output wl_pointer;Pointer wl_region;Region wl_registry;Registry wl_seat;Seat wl_shell;Shell wl_shell_surface;ShellSurface wl_shm;ShmPool wl_subcompositor;SubCompositor wl_subsurface;SubSurface wl_surface;Surface wl_touch;Touch _wl_fullscreen_shell;FullscreenShell org_kde_kwin_blur;Blur org_kde_kwin_blur_manager;BlurManager org_kde_kwin_contrast;Contrast org_kde_kwin_contrast_manager;ContrastManager org_kde_kwin_slide;Slide org_kde_kwin_slide_manager;SlideManager org_kde_kwin_fake_input;FakeInput org_kde_kwin_idle;Idle org_kde_kwin_idle_timeout;IdleTimeout org_kde_kwin_outputmanagement;OutputManagement org_kde_kwin_outputconfiguration;OutputConfiguration org_kde_kwin_outputdevice;OutputDevice org_kde_kwin_shadow;Shadow org_kde_kwin_shadow_manager;ShadowManager org_kde_plasma_shell;PlasmaShell org_kde_plasma_surface;PlasmaShellSurface org_kde_plasma_window_management;PlasmaWindowManagement org_kde_plasma_window;PlasmaWindow org_kde_kwin_server_decoration_manager;ServerSideDecorationManager org_kde_kwin_server_decoration;ServerSideDecoration wl_text_input;TextInputUnstableV0 wl_text_input_manager;TextInputManagerUnstableV0 zwp_text_input_v2;TextInputUnstableV2 zwp_text_input_manager_v2;TextInputManagerUnstableV2 xdg_shell;XdgShellV5 xdg_surface;XdgSurfaceV5 xdg_popup;XdgPopupV5 zxdg_shell_v6;XdgShellV6 zxdg_surface_v6;XdgSurfaceV6 zxdg_popup_v6;XdgPopupV6 zwp_relative_pointer_manager_v1;RelativePointerManagerUnstableV1 zwp_relative_pointer_v1;RelativePointerUnstableV1 zwp_pointer_gestures_v1;PointerGesturesUnstableV1 zwp_pointer_gesture_swipe_v1;PointerSwipeGestureUnstableV1 zwp_pointer_gesture_pinch_v1;PointerPinchGestureUnstableV1 zwp_pointer_constraints_v1;PointerConstraints zwp_locked_pointer_v1;LockedPointer zwp_confined_pointer_v1;ConfinedPointer +zwp_idle_inhibit_manager_v1;IdleInhibitManager +zwp_idle_inhibitor_v1;IdleInhibitor org_kde_kwin_remote_access_manager;RemoteAccessManager org_kde_kwin_remote_buffer;RemoteBuffer