diff --git a/CMakeLists.txt b/CMakeLists.txt index e1f2da4..1391d9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,94 +1,94 @@ cmake_minimum_required(VERSION 3.0) -set(KF5_VERSION "5.36.0") # handled by release scripts +set(KF5_VERSION "5.37.0") # handled by release scripts project(KWayland VERSION ${KF5_VERSION}) # ECM setup include(FeatureSummary) -find_package(ECM 5.35.0 NO_MODULE) +find_package(ECM 5.37.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.6.0) +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) 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 0d72a2f..c584e6b 100644 --- a/autotests/client/CMakeLists.txt +++ b/autotests/client/CMakeLists.txt @@ -1,370 +1,370 @@ ######################################################## # 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(kwayland-testWaylandConnectionThread testWaylandConnectionThread) +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(kwayland-testWaylandRegistry testWaylandRegistry) +add_test(NAME kwayland-testWaylandRegistry COMMAND testWaylandRegistry) ecm_mark_as_test(testWaylandRegistry) ######################################################## # Test WaylandFullscreenShell ######################################################## if(Wayland_VERSION VERSION_GREATER "1.4.0") 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(kwayland-testWaylandFullscreenShell testWaylandFullscreenShell) + add_test(NAME kwayland-testWaylandFullscreenShell COMMAND testWaylandFullscreenShell) ecm_mark_as_test(testWaylandFullscreenShell) 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(kwayland-testWaylandOutput testWaylandOutput) +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(kwayland-testWaylandShell testWaylandShell) +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(kwayland-testWaylandSurface testWaylandSurface) +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(kwayland-testWaylandSeat testWaylandSeat) + 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(kwayland-testShmPool testShmPool) +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(kwayland-testWaylandOutputManagement testWaylandOutputManagement) +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(kwayland-testWaylandOutputDevice testWaylandOutputDevice) +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(kwayland-testCompositor testCompositor) +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(kwayland-testSubCompositor testSubCompositor) +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(kwayland-testSubSurface testSubSurface) +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(kwayland-testRegion testRegion) +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(kwayland-testBlur testBlur) +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(kwayland-testContrast testContrast) +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(kwayland-testSlide testSlide) +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(kwayland-testWindowmanagement testWindowmanagement) +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(kwayland-testDataSource testDataSource) +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(kwayland-testDataDevice testDataDevice) +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(kwayland-testServerSideDecoration testServerSideDecoration) +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(kwayland-testDragAndDrop testDragAndDrop) +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(kwayland-testPlasmaShell testPlasmaShell) +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(kwayland-testIdle testIdle) +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(kwayland-testShadow testShadow) +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(kwayland-testFakeInput testFakeInput) + 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(kwayland-testPlasmaWindowModel testPlasmaWindowModel) + 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(kwayland-testTextInput testTextInput) +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(kwayland-testError testError) +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(kwayland-testSelection testSelection) +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(kwayland-testXdgShellV5 testXdgShellV5) +add_test(NAME kwayland-testXdgShellV5 COMMAND testXdgShellV5) ecm_mark_as_test(testXdgShellV5) ######################################################## # 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 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(kwayland-testPointerConstraints testPointerConstraints) +add_test(NAME kwayland-testPointerConstraints COMMAND testPointerConstraints) ecm_mark_as_test(testPointerConstraints) diff --git a/autotests/client/test_text_input.cpp b/autotests/client/test_text_input.cpp index 9b11aeb..baf5643 100644 --- a/autotests/client/test_text_input.cpp +++ b/autotests/client/test_text_input.cpp @@ -1,934 +1,936 @@ /******************************************************************** 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/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/keyboard.h" #include "../../src/client/registry.h" #include "../../src/client/seat.h" #include "../../src/client/surface.h" #include "../../src/client/textinput.h" // server #include "../../src/server/compositor_interface.h" #include "../../src/server/display.h" #include "../../src/server/seat_interface.h" #include "../../src/server/textinput_interface.h" using namespace KWayland::Client; using namespace KWayland::Server; class TextInputTest : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testEnterLeave_data(); void testEnterLeave(); void testShowHidePanel_data(); void testShowHidePanel(); void testCursorRectangle_data(); void testCursorRectangle(); void testPreferredLanguage_data(); void testPreferredLanguage(); void testReset_data(); void testReset(); void testSurroundingText_data(); void testSurroundingText(); void testContentHints_data(); void testContentHints(); void testContentPurpose_data(); void testContentPurpose(); void testTextDirection_data(); void testTextDirection(); void testLanguage_data(); void testLanguage(); void testKeyEvent_data(); void testKeyEvent(); void testPreEdit_data(); void testPreEdit(); void testCommit_data(); void testCommit(); private: SurfaceInterface *waitForSurface(); TextInput *createTextInput(TextInputInterfaceVersion version); Display *m_display = nullptr; SeatInterface *m_seatInterface = nullptr; CompositorInterface *m_compositorInterface = nullptr; TextInputManagerInterface *m_textInputManagerV0Interface = nullptr; TextInputManagerInterface *m_textInputManagerV2Interface = nullptr; ConnectionThread *m_connection = nullptr; QThread *m_thread = nullptr; EventQueue *m_queue = nullptr; Seat *m_seat = nullptr; Keyboard *m_keyboard = nullptr; Compositor *m_compositor = nullptr; TextInputManager *m_textInputManagerV0 = nullptr; TextInputManager *m_textInputManagerV2 = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-text-input-0"); void TextInputTest::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->setHasKeyboard(true); m_seatInterface->setHasTouch(true); m_seatInterface->create(); m_compositorInterface = m_display->createCompositor(); m_compositorInterface->create(); m_textInputManagerV0Interface = m_display->createTextInputManager(TextInputInterfaceVersion::UnstableV0); m_textInputManagerV0Interface->create(); m_textInputManagerV2Interface = m_display->createTextInputManager(TextInputInterfaceVersion::UnstableV2); m_textInputManagerV2Interface->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()); QSignalSpy hasKeyboardSpy(m_seat, &Seat::hasKeyboardChanged); QVERIFY(hasKeyboardSpy.isValid()); QVERIFY(hasKeyboardSpy.wait()); m_keyboard = m_seat->createKeyboard(this); QVERIFY(m_keyboard->isValid()); m_compositor = registry.createCompositor(registry.interface(Registry::Interface::Compositor).name, registry.interface(Registry::Interface::Compositor).version, this); QVERIFY(m_compositor->isValid()); m_textInputManagerV0 = registry.createTextInputManager(registry.interface(Registry::Interface::TextInputManagerUnstableV0).name, registry.interface(Registry::Interface::TextInputManagerUnstableV0).version, this); QVERIFY(m_textInputManagerV0->isValid()); m_textInputManagerV2 = registry.createTextInputManager(registry.interface(Registry::Interface::TextInputManagerUnstableV2).name, registry.interface(Registry::Interface::TextInputManagerUnstableV2).version, this); QVERIFY(m_textInputManagerV2->isValid()); } void TextInputTest::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(m_textInputManagerV0) CLEANUP(m_textInputManagerV2) CLEANUP(m_keyboard) CLEANUP(m_seat) CLEANUP(m_compositor) 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_textInputManagerV0Interface) CLEANUP(m_textInputManagerV2Interface) CLEANUP(m_compositorInterface) CLEANUP(m_seatInterface) CLEANUP(m_display) #undef CLEANUP } SurfaceInterface *TextInputTest::waitForSurface() { QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); if (!surfaceCreatedSpy.isValid()) { return nullptr; } if (!surfaceCreatedSpy.wait()) { return nullptr; } if (surfaceCreatedSpy.count() != 1) { return nullptr; } return surfaceCreatedSpy.first().first().value(); } TextInput *TextInputTest::createTextInput(TextInputInterfaceVersion version) { switch (version) { case TextInputInterfaceVersion::UnstableV0: return m_textInputManagerV0->createTextInput(m_seat); case TextInputInterfaceVersion::UnstableV2: return m_textInputManagerV2->createTextInput(m_seat); default: Q_UNREACHABLE(); return nullptr; } } void TextInputTest::testEnterLeave_data() { QTest::addColumn("version"); QTest::addColumn("updatesDirectly"); QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0 << false; QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2 << true; } void TextInputTest::testEnterLeave() { // this test verifies that enter leave are sent correctly QScopedPointer surface(m_compositor->createSurface()); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QVERIFY(!textInput.isNull()); QSignalSpy enteredSpy(textInput.data(), &TextInput::entered); QVERIFY(enteredSpy.isValid()); QSignalSpy leftSpy(textInput.data(), &TextInput::left); QVERIFY(leftSpy.isValid()); QSignalSpy textInputChangedSpy(m_seatInterface, &SeatInterface::focusedTextInputChanged); QVERIFY(textInputChangedSpy.isValid()); // now let's try to enter it QVERIFY(!m_seatInterface->focusedTextInput()); QVERIFY(!m_seatInterface->focusedTextInputSurface()); m_seatInterface->setFocusedKeyboardSurface(serverSurface); QCOMPARE(m_seatInterface->focusedTextInputSurface(), serverSurface); // text input not yet set for the surface QFETCH(bool, updatesDirectly); QCOMPARE(bool(m_seatInterface->focusedTextInput()), updatesDirectly); QCOMPARE(textInputChangedSpy.isEmpty(), !updatesDirectly); textInput->enable(surface.data()); // this should trigger on server side if (!updatesDirectly) { QVERIFY(textInputChangedSpy.wait()); } QCOMPARE(textInputChangedSpy.count(), 1); auto serverTextInput = m_seatInterface->focusedTextInput(); QVERIFY(serverTextInput); QCOMPARE(serverTextInput->interfaceVersion(), version); QSignalSpy enabledChangedSpy(serverTextInput, &TextInputInterface::enabledChanged); QVERIFY(enabledChangedSpy.isValid()); if (updatesDirectly) { QVERIFY(enabledChangedSpy.wait()); enabledChangedSpy.clear(); } QCOMPARE(serverTextInput->surface().data(), serverSurface); QVERIFY(serverTextInput->isEnabled()); // and trigger an enter if (enteredSpy.isEmpty()) { QVERIFY(enteredSpy.wait()); } QCOMPARE(enteredSpy.count(), 1); QCOMPARE(textInput->enteredSurface(), surface.data()); // now trigger a leave m_seatInterface->setFocusedKeyboardSurface(nullptr); QCOMPARE(textInputChangedSpy.count(), 2); QVERIFY(!m_seatInterface->focusedTextInput()); QVERIFY(leftSpy.wait()); QVERIFY(!textInput->enteredSurface()); QVERIFY(serverTextInput->isEnabled()); // if we enter again we should directly get the text input as it's still activated m_seatInterface->setFocusedKeyboardSurface(serverSurface); QCOMPARE(textInputChangedSpy.count(), 3); QVERIFY(m_seatInterface->focusedTextInput()); QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.count(), 2); QCOMPARE(textInput->enteredSurface(), surface.data()); QVERIFY(serverTextInput->isEnabled()); // let's deactivate on client side textInput->disable(surface.data()); QVERIFY(enabledChangedSpy.wait()); QCOMPARE(enabledChangedSpy.count(), 1); QVERIFY(!serverTextInput->isEnabled()); // does not trigger a leave QCOMPARE(textInputChangedSpy.count(), 3); // should still be the same text input QCOMPARE(m_seatInterface->focusedTextInput(), serverTextInput); //reset textInput->enable(surface.data()); QVERIFY(enabledChangedSpy.wait()); //trigger an enter again and leave, but this //time we try sending an event after the surface is unbound //but not yet destroyed. It should work without errors QCOMPARE(textInput->enteredSurface(), surface.data()); connect(serverSurface, &Resource::unbound, [=]() { m_seatInterface->setFocusedKeyboardSurface(nullptr); }); //delete the client and wait for the server to catch up QSignalSpy unboundSpy(serverSurface, &QObject::destroyed); surface.reset(); QVERIFY(unboundSpy.wait()); + QVERIFY(leftSpy.wait()); + QVERIFY(!textInput->enteredSurface()); } void TextInputTest::testShowHidePanel_data() { QTest::addColumn("version"); QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0; QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2; } void TextInputTest::testShowHidePanel() { // this test verifies that the requests for show/hide panel work // and that status is properly sent to the client QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); QSignalSpy showPanelRequestedSpy(ti, &TextInputInterface::requestShowInputPanel); QVERIFY(showPanelRequestedSpy.isValid()); QSignalSpy hidePanelRequestedSpy(ti, &TextInputInterface::requestHideInputPanel); QVERIFY(hidePanelRequestedSpy.isValid()); QSignalSpy inputPanelStateChangedSpy(textInput.data(), &TextInput::inputPanelStateChanged); QVERIFY(inputPanelStateChangedSpy.isValid()); QCOMPARE(textInput->isInputPanelVisible(), false); textInput->showInputPanel(); QVERIFY(showPanelRequestedSpy.wait()); ti->setInputPanelState(true, QRect(0, 0, 0, 0)); QVERIFY(inputPanelStateChangedSpy.wait()); QCOMPARE(textInput->isInputPanelVisible(), true); textInput->hideInputPanel(); QVERIFY(hidePanelRequestedSpy.wait()); ti->setInputPanelState(false, QRect(0, 0, 0, 0)); QVERIFY(inputPanelStateChangedSpy.wait()); QCOMPARE(textInput->isInputPanelVisible(), false); } void TextInputTest::testCursorRectangle_data() { QTest::addColumn("version"); QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0; QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2; } void TextInputTest::testCursorRectangle() { // this test verifies that passing the cursor rectangle from client to server works // and that setting visibility state from server to client works QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); QCOMPARE(ti->cursorRectangle(), QRect()); QSignalSpy cursorRectangleChangedSpy(ti, &TextInputInterface::cursorRectangleChanged); QVERIFY(cursorRectangleChangedSpy.isValid()); textInput->setCursorRectangle(QRect(10, 20, 30, 40)); QVERIFY(cursorRectangleChangedSpy.wait()); QCOMPARE(ti->cursorRectangle(), QRect(10, 20, 30, 40)); } void TextInputTest::testPreferredLanguage_data() { QTest::addColumn("version"); QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0; QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2; } void TextInputTest::testPreferredLanguage() { // this test verifies that passing the preferred language from client to server works QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); QVERIFY(ti->preferredLanguage().isEmpty()); QSignalSpy preferredLanguageChangedSpy(ti, &TextInputInterface::preferredLanguageChanged); QVERIFY(preferredLanguageChangedSpy.isValid()); textInput->setPreferredLanguage(QStringLiteral("foo")); QVERIFY(preferredLanguageChangedSpy.wait()); QCOMPARE(ti->preferredLanguage(), QStringLiteral("foo").toUtf8()); } void TextInputTest::testReset_data() { QTest::addColumn("version"); QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0; QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2; } void TextInputTest::testReset() { // this test verifies that the reset request is properly passed from client to server QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); QSignalSpy resetRequestedSpy(ti, &TextInputInterface::requestReset); QVERIFY(resetRequestedSpy.isValid()); textInput->reset(); QVERIFY(resetRequestedSpy.wait()); } void TextInputTest::testSurroundingText_data() { QTest::addColumn("version"); QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0; QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2; } void TextInputTest::testSurroundingText() { // this test verifies that surrounding text is properly passed around QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); QVERIFY(ti->surroundingText().isEmpty()); QCOMPARE(ti->surroundingTextCursorPosition(), 0); QCOMPARE(ti->surroundingTextSelectionAnchor(), 0); QSignalSpy surroundingTextChangedSpy(ti, &TextInputInterface::surroundingTextChanged); QVERIFY(surroundingTextChangedSpy.isValid()); textInput->setSurroundingText(QStringLiteral("100 €, 100 $"), 5, 6); QVERIFY(surroundingTextChangedSpy.wait()); QCOMPARE(ti->surroundingText(), QStringLiteral("100 €, 100 $").toUtf8()); QCOMPARE(ti->surroundingTextCursorPosition(), QStringLiteral("100 €, 100 $").toUtf8().indexOf(',')); QCOMPARE(ti->surroundingTextSelectionAnchor(), QStringLiteral("100 €, 100 $").toUtf8().indexOf(' ', ti->surroundingTextCursorPosition())); } void TextInputTest::testContentHints_data() { QTest::addColumn("version"); QTest::addColumn("clientHints"); QTest::addColumn("serverHints"); QTest::newRow("completion/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::AutoCompletion) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::AutoCompletion); QTest::newRow("Correction/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::AutoCorrection) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::AutoCorrection); QTest::newRow("Capitalization/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::AutoCapitalization) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::AutoCapitalization); QTest::newRow("Lowercase/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::LowerCase) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::LowerCase); QTest::newRow("Uppercase/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::UpperCase) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::UpperCase); QTest::newRow("Titlecase/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::TitleCase) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::TitleCase); QTest::newRow("HiddenText/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::HiddenText) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::HiddenText); QTest::newRow("SensitiveData/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::SensitiveData) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::SensitiveData); QTest::newRow("Latin/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::Latin) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::Latin); QTest::newRow("Multiline/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::MultiLine) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::MultiLine); QTest::newRow("autos/v0") << TextInputInterfaceVersion::UnstableV0 << (TextInput::ContentHint::AutoCompletion | TextInput::ContentHint::AutoCorrection | TextInput::ContentHint::AutoCapitalization) << (TextInputInterface::ContentHint::AutoCompletion | TextInputInterface::ContentHint::AutoCorrection | TextInputInterface::ContentHint::AutoCapitalization); // all has combinations which don't make sense - what's both lowercase and uppercase? QTest::newRow("all/v0") << TextInputInterfaceVersion::UnstableV0 << (TextInput::ContentHint::AutoCompletion | TextInput::ContentHint::AutoCorrection | TextInput::ContentHint::AutoCapitalization | TextInput::ContentHint::LowerCase | TextInput::ContentHint::UpperCase | TextInput::ContentHint::TitleCase | TextInput::ContentHint::HiddenText | TextInput::ContentHint::SensitiveData | TextInput::ContentHint::Latin | TextInput::ContentHint::MultiLine) << (TextInputInterface::ContentHint::AutoCompletion | TextInputInterface::ContentHint::AutoCorrection | TextInputInterface::ContentHint::AutoCapitalization | TextInputInterface::ContentHint::LowerCase | TextInputInterface::ContentHint::UpperCase | TextInputInterface::ContentHint::TitleCase | TextInputInterface::ContentHint::HiddenText | TextInputInterface::ContentHint::SensitiveData | TextInputInterface::ContentHint::Latin | TextInputInterface::ContentHint::MultiLine); // same for version 2 QTest::newRow("completion/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::AutoCompletion) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::AutoCompletion); QTest::newRow("Correction/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::AutoCorrection) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::AutoCorrection); QTest::newRow("Capitalization/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::AutoCapitalization) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::AutoCapitalization); QTest::newRow("Lowercase/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::LowerCase) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::LowerCase); QTest::newRow("Uppercase/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::UpperCase) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::UpperCase); QTest::newRow("Titlecase/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::TitleCase) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::TitleCase); QTest::newRow("HiddenText/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::HiddenText) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::HiddenText); QTest::newRow("SensitiveData/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::SensitiveData) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::SensitiveData); QTest::newRow("Latin/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::Latin) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::Latin); QTest::newRow("Multiline/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::MultiLine) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::MultiLine); QTest::newRow("autos/v2") << TextInputInterfaceVersion::UnstableV2 << (TextInput::ContentHint::AutoCompletion | TextInput::ContentHint::AutoCorrection | TextInput::ContentHint::AutoCapitalization) << (TextInputInterface::ContentHint::AutoCompletion | TextInputInterface::ContentHint::AutoCorrection | TextInputInterface::ContentHint::AutoCapitalization); // all has combinations which don't make sense - what's both lowercase and uppercase? QTest::newRow("all/v2") << TextInputInterfaceVersion::UnstableV2 << (TextInput::ContentHint::AutoCompletion | TextInput::ContentHint::AutoCorrection | TextInput::ContentHint::AutoCapitalization | TextInput::ContentHint::LowerCase | TextInput::ContentHint::UpperCase | TextInput::ContentHint::TitleCase | TextInput::ContentHint::HiddenText | TextInput::ContentHint::SensitiveData | TextInput::ContentHint::Latin | TextInput::ContentHint::MultiLine) << (TextInputInterface::ContentHint::AutoCompletion | TextInputInterface::ContentHint::AutoCorrection | TextInputInterface::ContentHint::AutoCapitalization | TextInputInterface::ContentHint::LowerCase | TextInputInterface::ContentHint::UpperCase | TextInputInterface::ContentHint::TitleCase | TextInputInterface::ContentHint::HiddenText | TextInputInterface::ContentHint::SensitiveData | TextInputInterface::ContentHint::Latin | TextInputInterface::ContentHint::MultiLine); } void TextInputTest::testContentHints() { // this test verifies that content hints are properly passed from client to server QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); QCOMPARE(ti->contentHints(), TextInputInterface::ContentHints()); QSignalSpy contentTypeChangedSpy(ti, &TextInputInterface::contentTypeChanged); QVERIFY(contentTypeChangedSpy.isValid()); QFETCH(TextInput::ContentHints, clientHints); textInput->setContentType(clientHints, TextInput::ContentPurpose::Normal); QVERIFY(contentTypeChangedSpy.wait()); QTEST(ti->contentHints(), "serverHints"); // setting to same should not trigger an update textInput->setContentType(clientHints, TextInput::ContentPurpose::Normal); QVERIFY(!contentTypeChangedSpy.wait(100)); // unsetting should work textInput->setContentType(TextInput::ContentHints(), TextInput::ContentPurpose::Normal); QVERIFY(contentTypeChangedSpy.wait()); QCOMPARE(ti->contentHints(), TextInputInterface::ContentHints()); } void TextInputTest::testContentPurpose_data() { QTest::addColumn("version"); QTest::addColumn("clientPurpose"); QTest::addColumn("serverPurpose"); QTest::newRow("Alpha/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Alpha << TextInputInterface::ContentPurpose::Alpha; QTest::newRow("Digits/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Digits << TextInputInterface::ContentPurpose::Digits; QTest::newRow("Number/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Number << TextInputInterface::ContentPurpose::Number; QTest::newRow("Phone/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Phone << TextInputInterface::ContentPurpose::Phone; QTest::newRow("Url/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Url << TextInputInterface::ContentPurpose::Url; QTest::newRow("Email/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Email << TextInputInterface::ContentPurpose::Email; QTest::newRow("Name/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Name << TextInputInterface::ContentPurpose::Name; QTest::newRow("Password/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Password << TextInputInterface::ContentPurpose::Password; QTest::newRow("Date/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Date << TextInputInterface::ContentPurpose::Date; QTest::newRow("Time/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Time << TextInputInterface::ContentPurpose::Time; QTest::newRow("Datetime/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::DateTime << TextInputInterface::ContentPurpose::DateTime; QTest::newRow("Terminal/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Terminal << TextInputInterface::ContentPurpose::Terminal; QTest::newRow("Alpha/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Alpha << TextInputInterface::ContentPurpose::Alpha; QTest::newRow("Digits/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Digits << TextInputInterface::ContentPurpose::Digits; QTest::newRow("Number/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Number << TextInputInterface::ContentPurpose::Number; QTest::newRow("Phone/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Phone << TextInputInterface::ContentPurpose::Phone; QTest::newRow("Url/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Url << TextInputInterface::ContentPurpose::Url; QTest::newRow("Email/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Email << TextInputInterface::ContentPurpose::Email; QTest::newRow("Name/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Name << TextInputInterface::ContentPurpose::Name; QTest::newRow("Password/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Password << TextInputInterface::ContentPurpose::Password; QTest::newRow("Date/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Date << TextInputInterface::ContentPurpose::Date; QTest::newRow("Time/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Time << TextInputInterface::ContentPurpose::Time; QTest::newRow("Datetime/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::DateTime << TextInputInterface::ContentPurpose::DateTime; QTest::newRow("Terminal/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Terminal << TextInputInterface::ContentPurpose::Terminal; } void TextInputTest::testContentPurpose() { // this test verifies that content purpose are properly passed from client to server QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); QCOMPARE(ti->contentPurpose(), TextInputInterface::ContentPurpose::Normal); QSignalSpy contentTypeChangedSpy(ti, &TextInputInterface::contentTypeChanged); QVERIFY(contentTypeChangedSpy.isValid()); QFETCH(TextInput::ContentPurpose, clientPurpose); textInput->setContentType(TextInput::ContentHints(), clientPurpose); QVERIFY(contentTypeChangedSpy.wait()); QTEST(ti->contentPurpose(), "serverPurpose"); // setting to same should not trigger an update textInput->setContentType(TextInput::ContentHints(), clientPurpose); QVERIFY(!contentTypeChangedSpy.wait(100)); // unsetting should work textInput->setContentType(TextInput::ContentHints(), TextInput::ContentPurpose::Normal); QVERIFY(contentTypeChangedSpy.wait()); QCOMPARE(ti->contentPurpose(), TextInputInterface::ContentPurpose::Normal); } void TextInputTest::testTextDirection_data() { QTest::addColumn("version"); QTest::addColumn("textDirection"); QTest::newRow("ltr/v0") << TextInputInterfaceVersion::UnstableV0 << Qt::LeftToRight; QTest::newRow("rtl/v0") << TextInputInterfaceVersion::UnstableV0 << Qt::RightToLeft; QTest::newRow("ltr/v2") << TextInputInterfaceVersion::UnstableV2 << Qt::LeftToRight; QTest::newRow("rtl/v2") << TextInputInterfaceVersion::UnstableV2 << Qt::RightToLeft; } void TextInputTest::testTextDirection() { // this test verifies that the text direction is sent from server to client QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); // default should be auto QCOMPARE(textInput->textDirection(), Qt::LayoutDirectionAuto); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); // let's send the new text direction QSignalSpy textDirectionChangedSpy(textInput.data(), &TextInput::textDirectionChanged); QVERIFY(textDirectionChangedSpy.isValid()); QFETCH(Qt::LayoutDirection, textDirection); ti->setTextDirection(textDirection); QVERIFY(textDirectionChangedSpy.wait()); QCOMPARE(textInput->textDirection(), textDirection); // setting again should not change ti->setTextDirection(textDirection); QVERIFY(!textDirectionChangedSpy.wait(100)); // setting back to auto ti->setTextDirection(Qt::LayoutDirectionAuto); QVERIFY(textDirectionChangedSpy.wait()); QCOMPARE(textInput->textDirection(), Qt::LayoutDirectionAuto); } void TextInputTest::testLanguage_data() { QTest::addColumn("version"); QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0; QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2; } void TextInputTest::testLanguage() { // this test verifies that language is sent from server to client QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); // default should be empty QVERIFY(textInput->language().isEmpty()); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); // let's send the new language QSignalSpy langugageChangedSpy(textInput.data(), &TextInput::languageChanged); QVERIFY(langugageChangedSpy.isValid()); ti->setLanguage(QByteArrayLiteral("foo")); QVERIFY(langugageChangedSpy.wait()); QCOMPARE(textInput->language(), QByteArrayLiteral("foo")); // setting to same should not trigger ti->setLanguage(QByteArrayLiteral("foo")); QVERIFY(!langugageChangedSpy.wait(100)); // but to something else should trigger again ti->setLanguage(QByteArrayLiteral("bar")); QVERIFY(langugageChangedSpy.wait()); QCOMPARE(textInput->language(), QByteArrayLiteral("bar")); } void TextInputTest::testKeyEvent_data() { QTest::addColumn("version"); QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0; QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2; } void TextInputTest::testKeyEvent() { qRegisterMetaType(); qRegisterMetaType(); // this test verifies that key events are properly sent to the client QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); // TODO: test modifiers QSignalSpy keyEventSpy(textInput.data(), &TextInput::keyEvent); QVERIFY(keyEventSpy.isValid()); m_seatInterface->setTimestamp(100); ti->keysymPressed(2); QVERIFY(keyEventSpy.wait()); QCOMPARE(keyEventSpy.count(), 1); QCOMPARE(keyEventSpy.last().at(0).value(), 2u); QCOMPARE(keyEventSpy.last().at(1).value(), TextInput::KeyState::Pressed); QCOMPARE(keyEventSpy.last().at(2).value(), Qt::KeyboardModifiers()); QCOMPARE(keyEventSpy.last().at(3).value(), 100u); m_seatInterface->setTimestamp(101); ti->keysymReleased(2); QVERIFY(keyEventSpy.wait()); QCOMPARE(keyEventSpy.count(), 2); QCOMPARE(keyEventSpy.last().at(0).value(), 2u); QCOMPARE(keyEventSpy.last().at(1).value(), TextInput::KeyState::Released); QCOMPARE(keyEventSpy.last().at(2).value(), Qt::KeyboardModifiers()); QCOMPARE(keyEventSpy.last().at(3).value(), 101u); } void TextInputTest::testPreEdit_data() { QTest::addColumn("version"); QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0; QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2; } void TextInputTest::testPreEdit() { // this test verifies that pre-edit is correctly passed to the client QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); // verify default values QVERIFY(textInput->composingText().isEmpty()); QVERIFY(textInput->composingFallbackText().isEmpty()); QCOMPARE(textInput->composingTextCursorPosition(), 0); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); // now let's pass through some pre-edit events QSignalSpy composingTextChangedSpy(textInput.data(), &TextInput::composingTextChanged); QVERIFY(composingTextChangedSpy.isValid()); ti->setPreEditCursor(1); ti->preEdit(QByteArrayLiteral("foo"), QByteArrayLiteral("bar")); QVERIFY(composingTextChangedSpy.wait()); QCOMPARE(composingTextChangedSpy.count(), 1); QCOMPARE(textInput->composingText(), QByteArrayLiteral("foo")); QCOMPARE(textInput->composingFallbackText(), QByteArrayLiteral("bar")); QCOMPARE(textInput->composingTextCursorPosition(), 1); // when no pre edit cursor is sent, it's at end of text ti->preEdit(QByteArrayLiteral("foobar"), QByteArray()); QVERIFY(composingTextChangedSpy.wait()); QCOMPARE(composingTextChangedSpy.count(), 2); QCOMPARE(textInput->composingText(), QByteArrayLiteral("foobar")); QCOMPARE(textInput->composingFallbackText(), QByteArray()); QCOMPARE(textInput->composingTextCursorPosition(), 6); } void TextInputTest::testCommit_data() { QTest::addColumn("version"); QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0; QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2; } void TextInputTest::testCommit() { // this test verifies that the commit is handled correctly by the client QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); // verify default values QCOMPARE(textInput->commitText(), QByteArray()); QCOMPARE(textInput->cursorPosition(), 0); QCOMPARE(textInput->anchorPosition(), 0); QCOMPARE(textInput->deleteSurroundingText().beforeLength, 0u); QCOMPARE(textInput->deleteSurroundingText().afterLength, 0u); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); // now let's commit QSignalSpy committedSpy(textInput.data(), &TextInput::committed); QVERIFY(committedSpy.isValid()); ti->setCursorPosition(3, 4); ti->deleteSurroundingText(2, 1); ti->commit(QByteArrayLiteral("foo")); QVERIFY(committedSpy.wait()); QCOMPARE(textInput->commitText(), QByteArrayLiteral("foo")); QCOMPARE(textInput->cursorPosition(), 3); QCOMPARE(textInput->anchorPosition(), 4); QCOMPARE(textInput->deleteSurroundingText().beforeLength, 2u); QCOMPARE(textInput->deleteSurroundingText().afterLength, 1u); } QTEST_GUILESS_MAIN(TextInputTest) #include "test_text_input.moc" diff --git a/autotests/client/test_wayland_connection_thread.cpp b/autotests/client/test_wayland_connection_thread.cpp index 67002d0..f7529f7 100644 --- a/autotests/client/test_wayland_connection_thread.cpp +++ b/autotests/client/test_wayland_connection_thread.cpp @@ -1,296 +1,301 @@ /******************************************************************** 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/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/registry.h" #include "../../src/server/display.h" // Wayland #include // system #include #include #include class TestWaylandConnectionThread : public QObject { Q_OBJECT public: explicit TestWaylandConnectionThread(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testInitConnectionNoThread(); void testConnectionFailure(); void testConnectionDieing(); void testConnectionThread(); void testConnectFd(); void testConnectFdNoSocketName(); private: KWayland::Server::Display *m_display; }; static const QString s_socketName = QStringLiteral("kwin-test-wayland-connection-0"); TestWaylandConnectionThread::TestWaylandConnectionThread(QObject *parent) : QObject(parent) , m_display(nullptr) { } void TestWaylandConnectionThread::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(); } void TestWaylandConnectionThread::cleanup() { delete m_display; m_display = nullptr; } void TestWaylandConnectionThread::testInitConnectionNoThread() { + QVERIFY(KWayland::Client::ConnectionThread::connections().isEmpty()); QScopedPointer connection(new KWayland::Client::ConnectionThread); + QVERIFY(KWayland::Client::ConnectionThread::connections().contains(connection.data())); QCOMPARE(connection->socketName(), QStringLiteral("wayland-0")); connection->setSocketName(s_socketName); QCOMPARE(connection->socketName(), s_socketName); QSignalSpy connectedSpy(connection.data(), SIGNAL(connected())); QSignalSpy failedSpy(connection.data(), SIGNAL(failed())); connection->initConnection(); QVERIFY(connectedSpy.wait()); QCOMPARE(connectedSpy.count(), 1); QCOMPARE(failedSpy.count(), 0); QVERIFY(connection->display()); + + connection.reset(); + QVERIFY(KWayland::Client::ConnectionThread::connections().isEmpty()); } void TestWaylandConnectionThread::testConnectionFailure() { QScopedPointer connection(new KWayland::Client::ConnectionThread); connection->setSocketName(QStringLiteral("kwin-test-socket-does-not-exist")); QSignalSpy connectedSpy(connection.data(), SIGNAL(connected())); QSignalSpy failedSpy(connection.data(), SIGNAL(failed())); connection->initConnection(); QVERIFY(failedSpy.wait()); QCOMPARE(connectedSpy.count(), 0); QCOMPARE(failedSpy.count(), 1); QVERIFY(!connection->display()); } static void registryHandleGlobal(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { Q_UNUSED(data) Q_UNUSED(registry) Q_UNUSED(name) Q_UNUSED(interface) Q_UNUSED(version) } static void registryHandleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name) { Q_UNUSED(data) Q_UNUSED(registry) Q_UNUSED(name) } static const struct wl_registry_listener s_registryListener = { registryHandleGlobal, registryHandleGlobalRemove }; void TestWaylandConnectionThread::testConnectionThread() { KWayland::Client::ConnectionThread *connection = new KWayland::Client::ConnectionThread; connection->setSocketName(s_socketName); QThread *connectionThread = new QThread(this); connection->moveToThread(connectionThread); connectionThread->start(); QSignalSpy connectedSpy(connection, SIGNAL(connected())); QVERIFY(connectedSpy.isValid()); QSignalSpy failedSpy(connection, SIGNAL(failed())); QVERIFY(failedSpy.isValid()); connection->initConnection(); QVERIFY(connectedSpy.wait()); QCOMPARE(connectedSpy.count(), 1); QCOMPARE(failedSpy.count(), 0); QVERIFY(connection->display()); // now we have the connection ready, let's get some events QSignalSpy eventsSpy(connection, SIGNAL(eventsRead())); QVERIFY(eventsSpy.isValid()); wl_display *display = connection->display(); QScopedPointer queue(new KWayland::Client::EventQueue); queue->setup(display); QVERIFY(queue->isValid()); connect(connection, &KWayland::Client::ConnectionThread::eventsRead, queue.data(), &KWayland::Client::EventQueue::dispatch, Qt::QueuedConnection); wl_registry *registry = wl_display_get_registry(display); wl_proxy_set_queue((wl_proxy*)registry, *(queue.data())); wl_registry_add_listener(registry, &s_registryListener, this); wl_display_flush(display); if (eventsSpy.isEmpty()) { QVERIFY(eventsSpy.wait()); } QVERIFY(!eventsSpy.isEmpty()); wl_registry_destroy(registry); queue.reset(); connection->deleteLater(); connectionThread->quit(); connectionThread->wait(); delete connectionThread; } void TestWaylandConnectionThread::testConnectionDieing() { QScopedPointer connection(new KWayland::Client::ConnectionThread); QSignalSpy connectedSpy(connection.data(), SIGNAL(connected())); connection->setSocketName(s_socketName); connection->initConnection(); QVERIFY(connectedSpy.wait()); QVERIFY(connection->display()); QSignalSpy diedSpy(connection.data(), SIGNAL(connectionDied())); m_display->terminate(); QVERIFY(!m_display->isRunning()); QVERIFY(diedSpy.wait()); QCOMPARE(diedSpy.count(), 1); QVERIFY(!connection->display()); connectedSpy.clear(); QVERIFY(connectedSpy.isEmpty()); // restarts the server m_display->start(); QVERIFY(m_display->isRunning()); if (connectedSpy.count() == 0) { QVERIFY(connectedSpy.wait()); } QCOMPARE(connectedSpy.count(), 1); } void TestWaylandConnectionThread::testConnectFd() { using namespace KWayland::Client; using namespace KWayland::Server; int sv[2]; QVERIFY(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) >= 0); auto c = m_display->createClient(sv[0]); QVERIFY(c); QSignalSpy disconnectedSpy(c, &ClientConnection::disconnected); QVERIFY(disconnectedSpy.isValid()); ConnectionThread *connection = new ConnectionThread; QSignalSpy connectedSpy(connection, SIGNAL(connected())); QVERIFY(connectedSpy.isValid()); connection->setSocketFd(sv[1]); QThread *connectionThread = new QThread(this); connection->moveToThread(connectionThread); connectionThread->start(); connection->initConnection(); QVERIFY(connectedSpy.wait()); // create the Registry QScopedPointer registry(new Registry); QSignalSpy announcedSpy(registry.data(), SIGNAL(interfacesAnnounced())); QVERIFY(announcedSpy.isValid()); registry->create(connection); QScopedPointer queue(new EventQueue); queue->setup(connection); registry->setEventQueue(queue.data()); registry->setup(); QVERIFY(announcedSpy.wait()); registry.reset(); queue.reset(); connection->deleteLater(); connectionThread->quit(); connectionThread->wait(); delete connectionThread; c->destroy(); QCOMPARE(disconnectedSpy.count(), 1); } void TestWaylandConnectionThread::testConnectFdNoSocketName() { delete m_display; m_display = nullptr; using namespace KWayland::Client; using namespace KWayland::Server; Display display; display.start(Display::StartMode::ConnectClientsOnly); QVERIFY(display.isRunning()); int sv[2]; QVERIFY(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) >= 0); QVERIFY(display.createClient(sv[0])); ConnectionThread *connection = new ConnectionThread; QSignalSpy connectedSpy(connection, SIGNAL(connected())); QVERIFY(connectedSpy.isValid()); connection->setSocketFd(sv[1]); QThread *connectionThread = new QThread(this); connection->moveToThread(connectionThread); connectionThread->start(); connection->initConnection(); QVERIFY(connectedSpy.wait()); // create the Registry QScopedPointer registry(new Registry); QSignalSpy announcedSpy(registry.data(), SIGNAL(interfacesAnnounced())); QVERIFY(announcedSpy.isValid()); registry->create(connection); QScopedPointer queue(new EventQueue); queue->setup(connection); registry->setEventQueue(queue.data()); registry->setup(); QVERIFY(announcedSpy.wait()); registry.reset(); queue.reset(); connection->deleteLater(); connectionThread->quit(); connectionThread->wait(); delete connectionThread; } QTEST_GUILESS_MAIN(TestWaylandConnectionThread) #include "test_wayland_connection_thread.moc" diff --git a/autotests/client/test_wayland_registry.cpp b/autotests/client/test_wayland_registry.cpp index f109d7d..7e050ed 100644 --- a/autotests/client/test_wayland_registry.cpp +++ b/autotests/client/test_wayland_registry.cpp @@ -1,731 +1,744 @@ /******************************************************************** 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/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/seat.h" #include "../../src/client/relativepointer.h" #include "../../src/client/server_decoration.h" #include "../../src/client/shell.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/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 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 testGlobalSync(); void testGlobalSyncThreaded(); void testRemoval(); 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; }; 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) { } 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_display->createBlurManager(this)->create(); m_display->createContrastManager(this)->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); } 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) } #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()); 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(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.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()); 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); 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()); 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); // 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); } 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.display()); + registry.create(&connection); registry.setup(); QVERIFY(registry.isValid()); - connect(&connection, &ConnectionThread::connectionDied, ®istry, &Registry::destroy); + //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_seat.cpp b/autotests/client/test_wayland_seat.cpp index 26e2152..a74005a 100644 --- a/autotests/client/test_wayland_seat.cpp +++ b/autotests/client/test_wayland_seat.cpp @@ -1,2289 +1,2293 @@ /******************************************************************** 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/datadevice.h" #include "../../src/client/datadevicemanager.h" #include "../../src/client/datasource.h" #include "../../src/client/event_queue.h" #include "../../src/client/keyboard.h" #include "../../src/client/pointer.h" #include "../../src/client/pointergestures.h" #include "../../src/client/surface.h" #include "../../src/client/registry.h" #include "../../src/client/relativepointer.h" #include "../../src/client/seat.h" #include "../../src/client/shm_pool.h" #include "../../src/client/subcompositor.h" #include "../../src/client/subsurface.h" #include "../../src/client/touch.h" #include "../../src/server/buffer_interface.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/datadevicemanager_interface.h" #include "../../src/server/display.h" #include "../../src/server/keyboard_interface.h" #include "../../src/server/pointer_interface.h" #include "../../src/server/pointergestures_interface.h" #include "../../src/server/relativepointer_interface.h" #include "../../src/server/seat_interface.h" #include "../../src/server/subcompositor_interface.h" #include "../../src/server/surface_interface.h" // Wayland #include #include // System #include #include class TestWaylandSeat : public QObject { Q_OBJECT public: explicit TestWaylandSeat(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testName(); void testCapabilities_data(); void testCapabilities(); void testPointer(); void testPointerTransformation_data(); void testPointerTransformation(); void testPointerButton_data(); void testPointerButton(); void testPointerSubSurfaceTree(); void testPointerSwipeGesture_data(); void testPointerSwipeGesture(); void testPointerPinchGesture_data(); void testPointerPinchGesture(); void testKeyboardSubSurfaceTreeFromPointer(); void testCursor(); void testCursorDamage(); void testKeyboard(); void testCast(); void testDestroy(); void testSelection(); void testSelectionNoDataSource(); void testDataDeviceForKeyboardSurface(); void testTouch(); void testDisconnect(); void testPointerEnterOnUnboundSurface(); // TODO: add test for keymap private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Server::SeatInterface *m_seatInterface; KWayland::Server::SubCompositorInterface *m_subCompositorInterface; KWayland::Server::RelativePointerManagerInterface *m_relativePointerManagerInterface; KWayland::Server::PointerGesturesInterface *m_pointerGesturesInterface; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::Seat *m_seat; KWayland::Client::ShmPool *m_shm; KWayland::Client::SubCompositor * m_subCompositor; KWayland::Client::RelativePointerManager *m_relativePointerManager; KWayland::Client::PointerGestures *m_pointerGestures; KWayland::Client::EventQueue *m_queue; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwin-test-wayland-seat-0"); TestWaylandSeat::TestWaylandSeat(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) , m_seatInterface(nullptr) , m_subCompositorInterface(nullptr) , m_relativePointerManagerInterface(nullptr) , m_pointerGesturesInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_seat(nullptr) , m_shm(nullptr) , m_subCompositor(nullptr) , m_relativePointerManager(nullptr) , m_pointerGestures(nullptr) , m_queue(nullptr) , m_thread(nullptr) { } void TestWaylandSeat::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_subCompositorInterface = m_display->createSubCompositor(m_display); QVERIFY(m_subCompositorInterface); m_subCompositorInterface->create(); QVERIFY(m_subCompositorInterface->isValid()); m_relativePointerManagerInterface = m_display->createRelativePointerManager(RelativePointerInterfaceVersion::UnstableV1, m_display); QVERIFY(m_relativePointerManagerInterface); m_relativePointerManagerInterface->create(); QVERIFY(m_relativePointerManagerInterface->isValid()); m_pointerGesturesInterface = m_display->createPointerGestures(PointerGesturesInterfaceVersion::UnstableV1, m_display); QVERIFY(m_pointerGesturesInterface); m_pointerGesturesInterface->create(); QVERIFY(m_pointerGesturesInterface->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(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new KWayland::Client::EventQueue(this); m_queue->setup(m_connection); KWayland::Client::Registry registry; QSignalSpy compositorSpy(®istry, SIGNAL(compositorAnnounced(quint32,quint32))); QSignalSpy seatSpy(®istry, SIGNAL(seatAnnounced(quint32,quint32))); QSignalSpy shmSpy(®istry, SIGNAL(shmAnnounced(quint32,quint32))); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(compositorSpy.wait()); m_seatInterface = m_display->createSeat(); QVERIFY(m_seatInterface); m_seatInterface->setName(QStringLiteral("seat0")); m_seatInterface->create(); QVERIFY(m_seatInterface->isValid()); QVERIFY(seatSpy.wait()); m_compositor = new KWayland::Client::Compositor(this); m_compositor->setup(registry.bindCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value())); QVERIFY(m_compositor->isValid()); m_seat = registry.createSeat(seatSpy.first().first().value(), seatSpy.first().last().value(), this); QSignalSpy nameSpy(m_seat, SIGNAL(nameChanged(QString))); QVERIFY(nameSpy.wait()); m_shm = new KWayland::Client::ShmPool(this); m_shm->setup(registry.bindShm(shmSpy.first().first().value(), shmSpy.first().last().value())); QVERIFY(m_shm->isValid()); m_subCompositor = registry.createSubCompositor(registry.interface(KWayland::Client::Registry::Interface::SubCompositor).name, registry.interface(KWayland::Client::Registry::Interface::SubCompositor).version, this); QVERIFY(m_subCompositor->isValid()); m_relativePointerManager = registry.createRelativePointerManager(registry.interface(KWayland::Client::Registry::Interface::RelativePointerManagerUnstableV1).name, registry.interface(KWayland::Client::Registry::Interface::RelativePointerManagerUnstableV1).version, this); QVERIFY(m_relativePointerManager->isValid()); m_pointerGestures = registry.createPointerGestures(registry.interface(KWayland::Client::Registry::Interface::PointerGesturesUnstableV1).name, registry.interface(KWayland::Client::Registry::Interface::PointerGesturesUnstableV1).version, this); QVERIFY(m_pointerGestures->isValid()); } void TestWaylandSeat::cleanup() { if (m_pointerGestures) { delete m_pointerGestures; m_pointerGestures = nullptr; } if (m_relativePointerManager) { delete m_relativePointerManager; m_relativePointerManager = nullptr; } if (m_subCompositor) { delete m_subCompositor; m_subCompositor = nullptr; } if (m_shm) { delete m_shm; m_shm = 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_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_compositorInterface; m_compositorInterface = nullptr; delete m_seatInterface; m_seatInterface = nullptr; delete m_subCompositorInterface; m_subCompositorInterface = nullptr; delete m_relativePointerManagerInterface; m_relativePointerManagerInterface = nullptr; delete m_pointerGesturesInterface; m_pointerGesturesInterface = nullptr; delete m_display; m_display = nullptr; } void TestWaylandSeat::testName() { // no name set yet QCOMPARE(m_seat->name(), QStringLiteral("seat0")); QSignalSpy spy(m_seat, SIGNAL(nameChanged(QString))); QVERIFY(spy.isValid()); const QString name = QStringLiteral("foobar"); m_seatInterface->setName(name); QVERIFY(spy.wait()); QCOMPARE(m_seat->name(), name); QCOMPARE(spy.count(), 1); QCOMPARE(spy.first().first().toString(), name); } void TestWaylandSeat::testCapabilities_data() { QTest::addColumn("pointer"); QTest::addColumn("keyboard"); QTest::addColumn("touch"); QTest::newRow("none") << false << false << false; QTest::newRow("pointer") << true << false << false; QTest::newRow("keyboard") << false << true << false; QTest::newRow("touch") << false << false << true; QTest::newRow("pointer/keyboard") << true << true << false; QTest::newRow("pointer/touch") << true << false << true; QTest::newRow("keyboard/touch") << false << true << true; QTest::newRow("all") << true << true << true; } void TestWaylandSeat::testCapabilities() { QVERIFY(!m_seat->hasPointer()); QVERIFY(!m_seat->hasKeyboard()); QVERIFY(!m_seat->hasTouch()); QFETCH(bool, pointer); QFETCH(bool, keyboard); QFETCH(bool, touch); QSignalSpy pointerSpy(m_seat, SIGNAL(hasPointerChanged(bool))); QVERIFY(pointerSpy.isValid()); QSignalSpy keyboardSpy(m_seat, SIGNAL(hasKeyboardChanged(bool))); QVERIFY(keyboardSpy.isValid()); QSignalSpy touchSpy(m_seat, SIGNAL(hasTouchChanged(bool))); QVERIFY(touchSpy.isValid()); m_seatInterface->setHasPointer(pointer); m_seatInterface->setHasKeyboard(keyboard); m_seatInterface->setHasTouch(touch); // do processing QCOMPARE(pointerSpy.wait(1000), pointer); QCOMPARE(pointerSpy.isEmpty(), !pointer); if (!pointerSpy.isEmpty()) { QCOMPARE(pointerSpy.first().first().toBool(), pointer); } if (keyboardSpy.isEmpty()) { QCOMPARE(keyboardSpy.wait(1000), keyboard); } QCOMPARE(keyboardSpy.isEmpty(), !keyboard); if (!keyboardSpy.isEmpty()) { QCOMPARE(keyboardSpy.first().first().toBool(), keyboard); } if (touchSpy.isEmpty()) { QCOMPARE(touchSpy.wait(1000), touch); } QCOMPARE(touchSpy.isEmpty(), !touch); if (!touchSpy.isEmpty()) { QCOMPARE(touchSpy.first().first().toBool(), touch); } QCOMPARE(m_seat->hasPointer(), pointer); QCOMPARE(m_seat->hasKeyboard(), keyboard); QCOMPARE(m_seat->hasTouch(), touch); } void TestWaylandSeat::testPointer() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy pointerSpy(m_seat, SIGNAL(hasPointerChanged(bool))); QVERIFY(pointerSpy.isValid()); m_seatInterface->setHasPointer(true); QVERIFY(pointerSpy.wait()); QSignalSpy surfaceCreatedSpy(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(surfaceCreatedSpy.isValid()); Surface *s = m_compositor->createSurface(m_compositor); QVERIFY(surfaceCreatedSpy.wait()); SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); QSignalSpy focusedPointerChangedSpy(m_seatInterface, &SeatInterface::focusedPointerChanged); QVERIFY(focusedPointerChangedSpy.isValid()); m_seatInterface->setPointerPos(QPoint(20, 18)); m_seatInterface->setFocusedPointerSurface(serverSurface, QPoint(10, 15)); QCOMPARE(focusedPointerChangedSpy.count(), 1); QVERIFY(!focusedPointerChangedSpy.first().first().value()); // no pointer yet QVERIFY(m_seatInterface->focusedPointerSurface()); QVERIFY(!m_seatInterface->focusedPointer()); Pointer *p = m_seat->createPointer(m_seat); const Pointer &cp = *p; QVERIFY(p->isValid()); QScopedPointer relativePointer(m_relativePointerManager->createRelativePointer(p)); QVERIFY(relativePointer->isValid()); QSignalSpy pointerCreatedSpy(m_seatInterface, SIGNAL(pointerCreated(KWayland::Server::PointerInterface*))); QVERIFY(pointerCreatedSpy.isValid()); // once the pointer is created it should be set as the focused pointer QVERIFY(pointerCreatedSpy.wait()); QVERIFY(m_seatInterface->focusedPointer()); QCOMPARE(pointerCreatedSpy.first().first().value(), m_seatInterface->focusedPointer()); QCOMPARE(focusedPointerChangedSpy.count(), 2); QCOMPARE(focusedPointerChangedSpy.last().first().value(), m_seatInterface->focusedPointer()); m_seatInterface->setFocusedPointerSurface(nullptr); QCOMPARE(focusedPointerChangedSpy.count(), 3); QVERIFY(!focusedPointerChangedSpy.last().first().value()); serverSurface->client()->flush(); QTest::qWait(100); QSignalSpy enteredSpy(p, SIGNAL(entered(quint32,QPointF))); QVERIFY(enteredSpy.isValid()); QSignalSpy leftSpy(p, SIGNAL(left(quint32))); QVERIFY(leftSpy.isValid()); QSignalSpy motionSpy(p, SIGNAL(motion(QPointF,quint32))); QVERIFY(motionSpy.isValid()); QSignalSpy axisSpy(p, SIGNAL(axisChanged(quint32,KWayland::Client::Pointer::Axis,qreal))); QVERIFY(axisSpy.isValid()); QSignalSpy buttonSpy(p, SIGNAL(buttonStateChanged(quint32,quint32,quint32,KWayland::Client::Pointer::ButtonState))); QVERIFY(buttonSpy.isValid()); QSignalSpy relativeMotionSpy(relativePointer.data(), &RelativePointer::relativeMotion); QVERIFY(relativeMotionSpy.isValid()); QVERIFY(!p->enteredSurface()); QVERIFY(!cp.enteredSurface()); m_seatInterface->setFocusedPointerSurface(serverSurface, QPoint(10, 15)); QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface); QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.first().first().value(), m_display->serial()); QCOMPARE(enteredSpy.first().last().toPoint(), QPoint(10, 3)); PointerInterface *serverPointer = m_seatInterface->focusedPointer(); QVERIFY(serverPointer); QCOMPARE(p->enteredSurface(), s); QCOMPARE(cp.enteredSurface(), s); QCOMPARE(focusedPointerChangedSpy.count(), 4); QCOMPARE(focusedPointerChangedSpy.last().first().value(), serverPointer); // test motion m_seatInterface->setTimestamp(1); m_seatInterface->setPointerPos(QPoint(10, 16)); QVERIFY(motionSpy.wait()); QCOMPARE(motionSpy.first().first().toPoint(), QPoint(0, 1)); QCOMPARE(motionSpy.first().last().value(), quint32(1)); // test relative motion m_seatInterface->relativePointerMotion(QSizeF(1, 2), QSizeF(3, 4), quint64(-1)); QVERIFY(relativeMotionSpy.wait()); QCOMPARE(relativeMotionSpy.count(), 1); QCOMPARE(relativeMotionSpy.first().at(0).toSizeF(), QSizeF(1, 2)); QCOMPARE(relativeMotionSpy.first().at(1).toSizeF(), QSizeF(3, 4)); QCOMPARE(relativeMotionSpy.first().at(2).value(), quint64(-1)); // test axis m_seatInterface->setTimestamp(2); m_seatInterface->pointerAxis(Qt::Horizontal, 10); QVERIFY(axisSpy.wait()); m_seatInterface->setTimestamp(3); m_seatInterface->pointerAxis(Qt::Vertical, 20); QVERIFY(axisSpy.wait()); QCOMPARE(axisSpy.first().at(0).value(), quint32(2)); QCOMPARE(axisSpy.first().at(1).value(), Pointer::Axis::Horizontal); QCOMPARE(axisSpy.first().at(2).value(), qreal(10)); QCOMPARE(axisSpy.last().at(0).value(), quint32(3)); QCOMPARE(axisSpy.last().at(1).value(), Pointer::Axis::Vertical); QCOMPARE(axisSpy.last().at(2).value(), qreal(20)); // test button m_seatInterface->setTimestamp(4); m_seatInterface->pointerButtonPressed(1); QVERIFY(buttonSpy.wait()); QCOMPARE(buttonSpy.at(0).at(0).value(), m_display->serial()); m_seatInterface->setTimestamp(5); m_seatInterface->pointerButtonPressed(2); QVERIFY(buttonSpy.wait()); QCOMPARE(buttonSpy.at(1).at(0).value(), m_display->serial()); m_seatInterface->setTimestamp(6); m_seatInterface->pointerButtonReleased(2); QVERIFY(buttonSpy.wait()); QCOMPARE(buttonSpy.at(2).at(0).value(), m_display->serial()); m_seatInterface->setTimestamp(7); m_seatInterface->pointerButtonReleased(1); QVERIFY(buttonSpy.wait()); QCOMPARE(buttonSpy.count(), 4); // timestamp QCOMPARE(buttonSpy.at(0).at(1).value(), quint32(4)); // button QCOMPARE(buttonSpy.at(0).at(2).value(), quint32(1)); QCOMPARE(buttonSpy.at(0).at(3).value(), KWayland::Client::Pointer::ButtonState::Pressed); // timestamp QCOMPARE(buttonSpy.at(1).at(1).value(), quint32(5)); // button QCOMPARE(buttonSpy.at(1).at(2).value(), quint32(2)); QCOMPARE(buttonSpy.at(1).at(3).value(), KWayland::Client::Pointer::ButtonState::Pressed); QCOMPARE(buttonSpy.at(2).at(0).value(), m_seatInterface->pointerButtonSerial(2)); // timestamp QCOMPARE(buttonSpy.at(2).at(1).value(), quint32(6)); // button QCOMPARE(buttonSpy.at(2).at(2).value(), quint32(2)); QCOMPARE(buttonSpy.at(2).at(3).value(), KWayland::Client::Pointer::ButtonState::Released); QCOMPARE(buttonSpy.at(3).at(0).value(), m_seatInterface->pointerButtonSerial(1)); // timestamp QCOMPARE(buttonSpy.at(3).at(1).value(), quint32(7)); // button QCOMPARE(buttonSpy.at(3).at(2).value(), quint32(1)); QCOMPARE(buttonSpy.at(3).at(3).value(), KWayland::Client::Pointer::ButtonState::Released); // leave the surface m_seatInterface->setFocusedPointerSurface(nullptr); QCOMPARE(focusedPointerChangedSpy.count(), 5); QVERIFY(leftSpy.wait()); QCOMPARE(leftSpy.first().first().value(), m_display->serial()); QVERIFY(!p->enteredSurface()); QVERIFY(!cp.enteredSurface()); // now a relative motion should not be sent to the relative pointer m_seatInterface->relativePointerMotion(QSizeF(1, 2), QSizeF(3, 4), quint64(-1)); QVERIFY(!relativeMotionSpy.wait()); // enter it again m_seatInterface->setFocusedPointerSurface(serverSurface, QPoint(0, 0)); QCOMPARE(focusedPointerChangedSpy.count(), 6); QVERIFY(enteredSpy.wait()); QCOMPARE(p->enteredSurface(), s); QCOMPARE(cp.enteredSurface(), s); // send another relative motion event m_seatInterface->relativePointerMotion(QSizeF(4, 5), QSizeF(6, 7), quint64(1)); QVERIFY(relativeMotionSpy.wait()); QCOMPARE(relativeMotionSpy.count(), 2); QCOMPARE(relativeMotionSpy.last().at(0).toSizeF(), QSizeF(4, 5)); QCOMPARE(relativeMotionSpy.last().at(1).toSizeF(), QSizeF(6, 7)); QCOMPARE(relativeMotionSpy.last().at(2).value(), quint64(1)); // destroy the focused pointer QSignalSpy unboundSpy(serverPointer, &Resource::unbound); QVERIFY(unboundSpy.isValid()); QSignalSpy destroyedSpy(serverPointer, &Resource::destroyed); QVERIFY(destroyedSpy.isValid()); delete p; QVERIFY(unboundSpy.wait()); QCOMPARE(unboundSpy.count(), 1); QCOMPARE(destroyedSpy.count(), 0); // now test that calling into the methods in Seat does not crash QCOMPARE(m_seatInterface->focusedPointer(), serverPointer); QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface); m_seatInterface->setTimestamp(8); m_seatInterface->setPointerPos(QPoint(10, 15)); m_seatInterface->setTimestamp(9); m_seatInterface->pointerButtonPressed(1); m_seatInterface->setTimestamp(10); m_seatInterface->pointerButtonReleased(1); m_seatInterface->setTimestamp(11); m_seatInterface->pointerAxis(Qt::Horizontal, 10); m_seatInterface->setTimestamp(12); m_seatInterface->pointerAxis(Qt::Vertical, 20); m_seatInterface->setFocusedPointerSurface(nullptr); QCOMPARE(focusedPointerChangedSpy.count(), 7); m_seatInterface->setFocusedPointerSurface(serverSurface); QCOMPARE(focusedPointerChangedSpy.count(), 8); QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface); QVERIFY(!m_seatInterface->focusedPointer()); // and now destroy QVERIFY(destroyedSpy.wait()); QCOMPARE(unboundSpy.count(), 1); QCOMPARE(destroyedSpy.count(), 1); QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface); QVERIFY(!m_seatInterface->focusedPointer()); // create a pointer again p = m_seat->createPointer(m_seat); QVERIFY(focusedPointerChangedSpy.wait()); QCOMPARE(focusedPointerChangedSpy.count(), 9); QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface); serverPointer = m_seatInterface->focusedPointer(); QVERIFY(serverPointer); QSignalSpy entered2Spy(p, &Pointer::entered); QVERIFY(entered2Spy.wait()); QCOMPARE(p->enteredSurface(), s); + QSignalSpy leftSpy2(p, &Pointer::left); + QVERIFY(leftSpy2.isValid()); delete s; QVERIFY(!p->enteredSurface()); - wl_display_flush(m_connection->display()); - QVERIFY(focusedPointerChangedSpy.wait()); + QVERIFY(leftSpy2.wait()); QCOMPARE(focusedPointerChangedSpy.count(), 10); QVERIFY(!m_seatInterface->focusedPointerSurface()); QVERIFY(!m_seatInterface->focusedPointer()); } void TestWaylandSeat::testPointerTransformation_data() { QTest::addColumn("enterTransformation"); // global position at 20/18 QTest::addColumn("expectedEnterPoint"); // global position at 10/16 QTest::addColumn("expectedMovePoint"); QMatrix4x4 tm; tm.translate(-10, -15); QTest::newRow("translation") << tm << QPointF(10, 3) << QPointF(0, 1); QMatrix4x4 sm; sm.scale(2, 2); QTest::newRow("scale") << sm << QPointF(40, 36) << QPointF(20, 32); QMatrix4x4 rotate; rotate.rotate(90, 0, 0, 1); QTest::newRow("rotate") << rotate << QPointF(-18, 20) << QPointF(-16, 10); } void TestWaylandSeat::testPointerTransformation() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy pointerSpy(m_seat, &Seat::hasPointerChanged); QVERIFY(pointerSpy.isValid()); m_seatInterface->setHasPointer(true); QVERIFY(pointerSpy.wait()); QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); Surface *s = m_compositor->createSurface(m_compositor); QVERIFY(surfaceCreatedSpy.wait()); SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); m_seatInterface->setPointerPos(QPoint(20, 18)); QFETCH(QMatrix4x4, enterTransformation); m_seatInterface->setFocusedPointerSurface(serverSurface, enterTransformation); QCOMPARE(m_seatInterface->focusedPointerSurfaceTransformation(), enterTransformation); // no pointer yet QVERIFY(m_seatInterface->focusedPointerSurface()); QVERIFY(!m_seatInterface->focusedPointer()); Pointer *p = m_seat->createPointer(m_seat); const Pointer &cp = *p; QVERIFY(p->isValid()); QSignalSpy pointerCreatedSpy(m_seatInterface, &SeatInterface::pointerCreated); QVERIFY(pointerCreatedSpy.isValid()); // once the pointer is created it should be set as the focused pointer QVERIFY(pointerCreatedSpy.wait()); QVERIFY(m_seatInterface->focusedPointer()); QCOMPARE(pointerCreatedSpy.first().first().value(), m_seatInterface->focusedPointer()); m_seatInterface->setFocusedPointerSurface(nullptr); serverSurface->client()->flush(); QTest::qWait(100); QSignalSpy enteredSpy(p, &Pointer::entered); QVERIFY(enteredSpy.isValid()); QSignalSpy leftSpy(p, &Pointer::left); QVERIFY(leftSpy.isValid()); QSignalSpy motionSpy(p, &Pointer::motion); QVERIFY(motionSpy.isValid()); QVERIFY(!p->enteredSurface()); QVERIFY(!cp.enteredSurface()); m_seatInterface->setFocusedPointerSurface(serverSurface, enterTransformation); QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface); QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.first().first().value(), m_display->serial()); QTEST(enteredSpy.first().last().toPointF(), "expectedEnterPoint"); PointerInterface *serverPointer = m_seatInterface->focusedPointer(); QVERIFY(serverPointer); QCOMPARE(p->enteredSurface(), s); QCOMPARE(cp.enteredSurface(), s); // test motion m_seatInterface->setTimestamp(1); m_seatInterface->setPointerPos(QPoint(10, 16)); QVERIFY(motionSpy.wait()); QTEST(motionSpy.first().first().toPointF(), "expectedMovePoint"); QCOMPARE(motionSpy.first().last().value(), quint32(1)); // leave the surface m_seatInterface->setFocusedPointerSurface(nullptr); QVERIFY(leftSpy.wait()); QCOMPARE(leftSpy.first().first().value(), m_display->serial()); QVERIFY(!p->enteredSurface()); QVERIFY(!cp.enteredSurface()); // enter it again m_seatInterface->setFocusedPointerSurface(serverSurface); QVERIFY(enteredSpy.wait()); QCOMPARE(p->enteredSurface(), s); QCOMPARE(cp.enteredSurface(), s); delete s; wl_display_flush(m_connection->display()); QTest::qWait(100); QVERIFY(!m_seatInterface->focusedPointerSurface()); } Q_DECLARE_METATYPE(Qt::MouseButton) void TestWaylandSeat::testPointerButton_data() { QTest::addColumn("qtButton"); QTest::addColumn("waylandButton"); QTest::newRow("left") << Qt::LeftButton << quint32(BTN_LEFT); QTest::newRow("right") << Qt::RightButton << quint32(BTN_RIGHT); QTest::newRow("mid") << Qt::MidButton << quint32(BTN_MIDDLE); QTest::newRow("middle") << Qt::MiddleButton << quint32(BTN_MIDDLE); QTest::newRow("back") << Qt::BackButton << quint32(BTN_BACK); QTest::newRow("x1") << Qt::XButton1 << quint32(BTN_BACK); QTest::newRow("extra1") << Qt::ExtraButton1 << quint32(BTN_BACK); QTest::newRow("forward") << Qt::ForwardButton << quint32(BTN_FORWARD); QTest::newRow("x2") << Qt::XButton2 << quint32(BTN_FORWARD); QTest::newRow("extra2") << Qt::ExtraButton2 << quint32(BTN_FORWARD); QTest::newRow("task") << Qt::TaskButton << quint32(BTN_TASK); QTest::newRow("extra3") << Qt::ExtraButton3 << quint32(BTN_TASK); QTest::newRow("extra4") << Qt::ExtraButton4 << quint32(BTN_EXTRA); QTest::newRow("extra5") << Qt::ExtraButton5 << quint32(BTN_SIDE); QTest::newRow("extra6") << Qt::ExtraButton6 << quint32(0x118); QTest::newRow("extra7") << Qt::ExtraButton7 << quint32(0x119); QTest::newRow("extra8") << Qt::ExtraButton8 << quint32(0x11a); QTest::newRow("extra9") << Qt::ExtraButton9 << quint32(0x11b); QTest::newRow("extra10") << Qt::ExtraButton10 << quint32(0x11c); QTest::newRow("extra11") << Qt::ExtraButton11 << quint32(0x11d); QTest::newRow("extra12") << Qt::ExtraButton12 << quint32(0x11e); QTest::newRow("extra13") << Qt::ExtraButton13 << quint32(0x11f); } void TestWaylandSeat::testPointerButton() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy pointerSpy(m_seat, SIGNAL(hasPointerChanged(bool))); QVERIFY(pointerSpy.isValid()); m_seatInterface->setHasPointer(true); QVERIFY(pointerSpy.wait()); QSignalSpy surfaceCreatedSpy(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(surfaceCreatedSpy.isValid()); m_compositor->createSurface(m_compositor); QVERIFY(surfaceCreatedSpy.wait()); SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); QScopedPointer p(m_seat->createPointer()); QVERIFY(p->isValid()); QSignalSpy buttonChangedSpy(p.data(), SIGNAL(buttonStateChanged(quint32,quint32,quint32,KWayland::Client::Pointer::ButtonState))); QVERIFY(buttonChangedSpy.isValid()); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); m_seatInterface->setPointerPos(QPoint(20, 18)); m_seatInterface->setFocusedPointerSurface(serverSurface, QPoint(10, 15)); QVERIFY(m_seatInterface->focusedPointerSurface()); QVERIFY(m_seatInterface->focusedPointer()); QCoreApplication::processEvents(); m_seatInterface->setFocusedPointerSurface(serverSurface, QPoint(10, 15)); PointerInterface *serverPointer = m_seatInterface->focusedPointer(); QVERIFY(serverPointer); QFETCH(Qt::MouseButton, qtButton); QFETCH(quint32, waylandButton); quint32 msec = QDateTime::currentMSecsSinceEpoch(); QCOMPARE(m_seatInterface->isPointerButtonPressed(waylandButton), false); QCOMPARE(m_seatInterface->isPointerButtonPressed(qtButton), false); m_seatInterface->setTimestamp(msec); m_seatInterface->pointerButtonPressed(qtButton); QCOMPARE(m_seatInterface->isPointerButtonPressed(waylandButton), true); QCOMPARE(m_seatInterface->isPointerButtonPressed(qtButton), true); QVERIFY(buttonChangedSpy.wait()); QCOMPARE(buttonChangedSpy.count(), 1); QCOMPARE(buttonChangedSpy.last().at(0).value(), m_seatInterface->pointerButtonSerial(waylandButton)); QCOMPARE(buttonChangedSpy.last().at(0).value(), m_seatInterface->pointerButtonSerial(qtButton)); QCOMPARE(buttonChangedSpy.last().at(1).value(), msec); QCOMPARE(buttonChangedSpy.last().at(2).value(), waylandButton); QCOMPARE(buttonChangedSpy.last().at(3).value(), Pointer::ButtonState::Pressed); msec = QDateTime::currentMSecsSinceEpoch(); m_seatInterface->setTimestamp(QDateTime::currentMSecsSinceEpoch()); m_seatInterface->pointerButtonReleased(qtButton); QCOMPARE(m_seatInterface->isPointerButtonPressed(waylandButton), false); QCOMPARE(m_seatInterface->isPointerButtonPressed(qtButton), false); QVERIFY(buttonChangedSpy.wait()); QCOMPARE(buttonChangedSpy.count(), 2); QCOMPARE(buttonChangedSpy.last().at(0).value(), m_seatInterface->pointerButtonSerial(waylandButton)); QCOMPARE(buttonChangedSpy.last().at(0).value(), m_seatInterface->pointerButtonSerial(qtButton)); QCOMPARE(buttonChangedSpy.last().at(1).value(), msec); QCOMPARE(buttonChangedSpy.last().at(2).value(), waylandButton); QCOMPARE(buttonChangedSpy.last().at(3).value(), Pointer::ButtonState::Released); } void TestWaylandSeat::testPointerSubSurfaceTree() { // this test verifies that pointer motion on a surface with sub-surfaces sends motion enter/leave to the sub-surface using namespace KWayland::Client; using namespace KWayland::Server; // first create the pointer QSignalSpy hasPointerChangedSpy(m_seat, &Seat::hasPointerChanged); QVERIFY(hasPointerChangedSpy.isValid()); m_seatInterface->setHasPointer(true); QVERIFY(hasPointerChangedSpy.wait()); QScopedPointer pointer(m_seat->createPointer()); // create a sub surface tree // parent surface (100, 100) with one sub surface taking the half of it's size (50, 100) // which has two further children (50, 50) which are overlapping QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer parentSurface(m_compositor->createSurface()); QScopedPointer childSurface(m_compositor->createSurface()); QScopedPointer grandChild1Surface(m_compositor->createSurface()); QScopedPointer grandChild2Surface(m_compositor->createSurface()); QScopedPointer childSubSurface(m_subCompositor->createSubSurface(childSurface.data(), parentSurface.data())); QScopedPointer grandChild1SubSurface(m_subCompositor->createSubSurface(grandChild1Surface.data(), childSurface.data())); QScopedPointer grandChild2SubSurface(m_subCompositor->createSubSurface(grandChild2Surface.data(), childSurface.data())); grandChild2SubSurface->setPosition(QPoint(0, 25)); // let's map the surfaces auto render = [this] (Surface *s, const QSize &size) { QImage image(size, QImage::Format_ARGB32); image.fill(Qt::black); s->attachBuffer(m_shm->createBuffer(image)); s->damage(QRect(QPoint(0, 0), size)); s->commit(Surface::CommitFlag::None); }; render(grandChild2Surface.data(), QSize(50, 50)); render(grandChild1Surface.data(), QSize(50, 50)); render(childSurface.data(), QSize(50, 100)); render(parentSurface.data(), QSize(100, 100)); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface->isMapped()); // send in pointer events QSignalSpy enteredSpy(pointer.data(), &Pointer::entered); QVERIFY(enteredSpy.isValid()); QSignalSpy leftSpy(pointer.data(), &Pointer::left); QVERIFY(leftSpy.isValid()); QSignalSpy motionSpy(pointer.data(), &Pointer::motion); QVERIFY(motionSpy.isValid()); // first to the grandChild2 in the overlapped area quint32 timestamp = 1; m_seatInterface->setTimestamp(timestamp++); m_seatInterface->setPointerPos(QPointF(25, 50)); m_seatInterface->setFocusedPointerSurface(serverSurface); QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.count(), 1); QCOMPARE(leftSpy.count(), 0); QCOMPARE(motionSpy.count(), 0); QCOMPARE(enteredSpy.last().last().toPointF(), QPointF(25, 25)); QCOMPARE(pointer->enteredSurface(), grandChild2Surface.data()); // a motion on grandchild2 m_seatInterface->setTimestamp(timestamp++); m_seatInterface->setPointerPos(QPointF(25, 60)); QVERIFY(motionSpy.wait()); QCOMPARE(enteredSpy.count(), 1); QCOMPARE(leftSpy.count(), 0); QCOMPARE(motionSpy.count(), 1); QCOMPARE(motionSpy.last().first().toPointF(), QPointF(25, 35)); // motion which changes to childSurface m_seatInterface->setTimestamp(timestamp++); m_seatInterface->setPointerPos(QPointF(25, 80)); QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.count(), 2); QCOMPARE(leftSpy.count(), 1); QCOMPARE(motionSpy.count(), 1); QCOMPARE(enteredSpy.last().last().toPointF(), QPointF(25, 80)); QCOMPARE(pointer->enteredSurface(), childSurface.data()); // a leave for the whole surface m_seatInterface->setTimestamp(timestamp++); m_seatInterface->setFocusedPointerSurface(nullptr); QVERIFY(leftSpy.wait()); QCOMPARE(enteredSpy.count(), 2); QCOMPARE(leftSpy.count(), 2); QCOMPARE(motionSpy.count(), 1); // a new enter on the main surface m_seatInterface->setTimestamp(timestamp++); m_seatInterface->setPointerPos(QPointF(75, 50)); m_seatInterface->setFocusedPointerSurface(serverSurface); QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.count(), 3); QCOMPARE(leftSpy.count(), 2); QCOMPARE(motionSpy.count(), 1); QCOMPARE(enteredSpy.last().last().toPointF(), QPointF(75, 50)); QCOMPARE(pointer->enteredSurface(), parentSurface.data()); } void TestWaylandSeat::testPointerSwipeGesture_data() { QTest::addColumn("cancel"); QTest::addColumn("expectedEndCount"); QTest::addColumn("expectedCancelCount"); QTest::newRow("end") << false << 1 << 0; QTest::newRow("cancel") << true << 0 << 1; } void TestWaylandSeat::testPointerSwipeGesture() { using namespace KWayland::Client; using namespace KWayland::Server; // first create the pointer and pointer swipe gesture QSignalSpy hasPointerChangedSpy(m_seat, &Seat::hasPointerChanged); QVERIFY(hasPointerChangedSpy.isValid()); m_seatInterface->setHasPointer(true); QVERIFY(hasPointerChangedSpy.wait()); QScopedPointer pointer(m_seat->createPointer()); QScopedPointer gesture(m_pointerGestures->createSwipeGesture(pointer.data())); QVERIFY(gesture); QVERIFY(gesture->isValid()); QVERIFY(gesture->surface().isNull()); QCOMPARE(gesture->fingerCount(), 0u); QSignalSpy startSpy(gesture.data(), &PointerSwipeGesture::started); QVERIFY(startSpy.isValid()); QSignalSpy updateSpy(gesture.data(), &PointerSwipeGesture::updated); QVERIFY(updateSpy.isValid()); QSignalSpy endSpy(gesture.data(), &PointerSwipeGesture::ended); QVERIFY(endSpy.isValid()); QSignalSpy cancelledSpy(gesture.data(), &PointerSwipeGesture::cancelled); QVERIFY(cancelledSpy.isValid()); // now create a surface QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); m_seatInterface->setFocusedPointerSurface(serverSurface); QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface); QVERIFY(m_seatInterface->focusedPointer()); // send in the start quint32 timestamp = 1; m_seatInterface->setTimestamp(timestamp++); m_seatInterface->startPointerSwipeGesture(2); QVERIFY(startSpy.wait()); QCOMPARE(startSpy.count(), 1); QCOMPARE(startSpy.first().at(0).value(), m_display->serial()); QCOMPARE(startSpy.first().at(1).value(), 1u); QCOMPARE(gesture->fingerCount(), 2u); QCOMPARE(gesture->surface().data(), surface.data()); // another start should not be possible m_seatInterface->startPointerSwipeGesture(2); QVERIFY(!startSpy.wait()); // send in some updates m_seatInterface->setTimestamp(timestamp++); m_seatInterface->updatePointerSwipeGesture(QSizeF(2, 3)); QVERIFY(updateSpy.wait()); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->updatePointerSwipeGesture(QSizeF(4, 5)); QVERIFY(updateSpy.wait()); QCOMPARE(updateSpy.count(), 2); QCOMPARE(updateSpy.at(0).at(0).toSizeF(), QSizeF(2, 3)); QCOMPARE(updateSpy.at(0).at(1).value(), 2u); QCOMPARE(updateSpy.at(1).at(0).toSizeF(), QSizeF(4, 5)); QCOMPARE(updateSpy.at(1).at(1).value(), 3u); // now end or cancel QFETCH(bool, cancel); QSignalSpy *spy; m_seatInterface->setTimestamp(timestamp++); if (cancel) { m_seatInterface->cancelPointerSwipeGesture(); spy = &cancelledSpy; } else { m_seatInterface->endPointerSwipeGesture(); spy = &endSpy; } QVERIFY(spy->wait()); QTEST(endSpy.count(), "expectedEndCount"); QTEST(cancelledSpy.count(), "expectedCancelCount"); QCOMPARE(spy->count(), 1); QCOMPARE(spy->first().at(0).value(), m_display->serial()); QCOMPARE(spy->first().at(1).value(), 4u); QCOMPARE(gesture->fingerCount(), 0u); QVERIFY(gesture->surface().isNull()); // now a start should be possible again m_seatInterface->setTimestamp(timestamp++); m_seatInterface->startPointerSwipeGesture(2); QVERIFY(startSpy.wait()); // unsetting the focused pointer surface should not change anything m_seatInterface->setFocusedPointerSurface(nullptr); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->updatePointerSwipeGesture(QSizeF(6, 7)); QVERIFY(updateSpy.wait()); // and end m_seatInterface->setTimestamp(timestamp++); if (cancel) { m_seatInterface->cancelPointerSwipeGesture(); } else { m_seatInterface->endPointerSwipeGesture(); } QVERIFY(spy->wait()); } void TestWaylandSeat::testPointerPinchGesture_data() { QTest::addColumn("cancel"); QTest::addColumn("expectedEndCount"); QTest::addColumn("expectedCancelCount"); QTest::newRow("end") << false << 1 << 0; QTest::newRow("cancel") << true << 0 << 1; } void TestWaylandSeat::testPointerPinchGesture() { using namespace KWayland::Client; using namespace KWayland::Server; // first create the pointer and pointer swipe gesture QSignalSpy hasPointerChangedSpy(m_seat, &Seat::hasPointerChanged); QVERIFY(hasPointerChangedSpy.isValid()); m_seatInterface->setHasPointer(true); QVERIFY(hasPointerChangedSpy.wait()); QScopedPointer pointer(m_seat->createPointer()); QScopedPointer gesture(m_pointerGestures->createPinchGesture(pointer.data())); QVERIFY(gesture); QVERIFY(gesture->isValid()); QVERIFY(gesture->surface().isNull()); QCOMPARE(gesture->fingerCount(), 0u); QSignalSpy startSpy(gesture.data(), &PointerPinchGesture::started); QVERIFY(startSpy.isValid()); QSignalSpy updateSpy(gesture.data(), &PointerPinchGesture::updated); QVERIFY(updateSpy.isValid()); QSignalSpy endSpy(gesture.data(), &PointerPinchGesture::ended); QVERIFY(endSpy.isValid()); QSignalSpy cancelledSpy(gesture.data(), &PointerPinchGesture::cancelled); QVERIFY(cancelledSpy.isValid()); // now create a surface QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); m_seatInterface->setFocusedPointerSurface(serverSurface); QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface); QVERIFY(m_seatInterface->focusedPointer()); // send in the start quint32 timestamp = 1; m_seatInterface->setTimestamp(timestamp++); m_seatInterface->startPointerPinchGesture(3); QVERIFY(startSpy.wait()); QCOMPARE(startSpy.count(), 1); QCOMPARE(startSpy.first().at(0).value(), m_display->serial()); QCOMPARE(startSpy.first().at(1).value(), 1u); QCOMPARE(gesture->fingerCount(), 3u); QCOMPARE(gesture->surface().data(), surface.data()); // another start should not be possible m_seatInterface->startPointerPinchGesture(3); QVERIFY(!startSpy.wait()); // send in some updates m_seatInterface->setTimestamp(timestamp++); m_seatInterface->updatePointerPinchGesture(QSizeF(2, 3), 2, 45); QVERIFY(updateSpy.wait()); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->updatePointerPinchGesture(QSizeF(4, 5), 1, 90); QVERIFY(updateSpy.wait()); QCOMPARE(updateSpy.count(), 2); QCOMPARE(updateSpy.at(0).at(0).toSizeF(), QSizeF(2, 3)); QCOMPARE(updateSpy.at(0).at(1).value(), 2u); QCOMPARE(updateSpy.at(0).at(2).value(), 45u); QCOMPARE(updateSpy.at(0).at(3).value(), 2u); QCOMPARE(updateSpy.at(1).at(0).toSizeF(), QSizeF(4, 5)); QCOMPARE(updateSpy.at(1).at(1).value(), 1u); QCOMPARE(updateSpy.at(1).at(2).value(), 90u); QCOMPARE(updateSpy.at(1).at(3).value(), 3u); // now end or cancel QFETCH(bool, cancel); QSignalSpy *spy; m_seatInterface->setTimestamp(timestamp++); if (cancel) { m_seatInterface->cancelPointerPinchGesture(); spy = &cancelledSpy; } else { m_seatInterface->endPointerPinchGesture(); spy = &endSpy; } QVERIFY(spy->wait()); QTEST(endSpy.count(), "expectedEndCount"); QTEST(cancelledSpy.count(), "expectedCancelCount"); QCOMPARE(spy->count(), 1); QCOMPARE(spy->first().at(0).value(), m_display->serial()); QCOMPARE(spy->first().at(1).value(), 4u); QCOMPARE(gesture->fingerCount(), 0u); QVERIFY(gesture->surface().isNull()); // now a start should be possible again m_seatInterface->setTimestamp(timestamp++); m_seatInterface->startPointerPinchGesture(3); QVERIFY(startSpy.wait()); // unsetting the focused pointer surface should not change anything m_seatInterface->setFocusedPointerSurface(nullptr); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->updatePointerPinchGesture(QSizeF(6, 7), 2, -45); QVERIFY(updateSpy.wait()); // and end m_seatInterface->setTimestamp(timestamp++); if (cancel) { m_seatInterface->cancelPointerPinchGesture(); } else { m_seatInterface->endPointerPinchGesture(); } QVERIFY(spy->wait()); } void TestWaylandSeat::testKeyboardSubSurfaceTreeFromPointer() { // this test verifies that when clicking on a sub-surface the keyboard focus passes to it using namespace KWayland::Client; using namespace KWayland::Server; // first create the pointer QSignalSpy hasPointerChangedSpy(m_seat, &Seat::hasPointerChanged); QVERIFY(hasPointerChangedSpy.isValid()); m_seatInterface->setHasPointer(true); QVERIFY(hasPointerChangedSpy.wait()); QScopedPointer pointer(m_seat->createPointer()); // and create keyboard QSignalSpy hasKeyboardChangedSpy(m_seat, &Seat::hasKeyboardChanged); QVERIFY(hasKeyboardChangedSpy.isValid()); m_seatInterface->setHasKeyboard(true); QVERIFY(hasKeyboardChangedSpy.wait()); QScopedPointer keyboard(m_seat->createKeyboard()); // create a sub surface tree // parent surface (100, 100) with one sub surface taking the half of it's size (50, 100) // which has two further children (50, 50) which are overlapping QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer parentSurface(m_compositor->createSurface()); QScopedPointer childSurface(m_compositor->createSurface()); QScopedPointer grandChild1Surface(m_compositor->createSurface()); QScopedPointer grandChild2Surface(m_compositor->createSurface()); QScopedPointer childSubSurface(m_subCompositor->createSubSurface(childSurface.data(), parentSurface.data())); QScopedPointer grandChild1SubSurface(m_subCompositor->createSubSurface(grandChild1Surface.data(), childSurface.data())); QScopedPointer grandChild2SubSurface(m_subCompositor->createSubSurface(grandChild2Surface.data(), childSurface.data())); grandChild2SubSurface->setPosition(QPoint(0, 25)); // let's map the surfaces auto render = [this] (Surface *s, const QSize &size) { QImage image(size, QImage::Format_ARGB32); image.fill(Qt::black); s->attachBuffer(m_shm->createBuffer(image)); s->damage(QRect(QPoint(0, 0), size)); s->commit(Surface::CommitFlag::None); }; render(grandChild2Surface.data(), QSize(50, 50)); render(grandChild1Surface.data(), QSize(50, 50)); render(childSurface.data(), QSize(50, 100)); render(parentSurface.data(), QSize(100, 100)); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface->isMapped()); // pass keyboard focus to the main surface QSignalSpy enterSpy(keyboard.data(), &Keyboard::entered); QVERIFY(enterSpy.isValid()); QSignalSpy leftSpy(keyboard.data(), &Keyboard::left); QVERIFY(leftSpy.isValid()); m_seatInterface->setFocusedKeyboardSurface(serverSurface); QVERIFY(enterSpy.wait()); QCOMPARE(enterSpy.count(), 1); QCOMPARE(leftSpy.count(), 0); QCOMPARE(keyboard->enteredSurface(), parentSurface.data()); // now pass also pointer focus to the surface QSignalSpy pointerEnterSpy(pointer.data(), &Pointer::entered); QVERIFY(pointerEnterSpy.isValid()); quint32 timestamp = 1; m_seatInterface->setTimestamp(timestamp++); m_seatInterface->setPointerPos(QPointF(25, 50)); m_seatInterface->setFocusedPointerSurface(serverSurface); QVERIFY(pointerEnterSpy.wait()); QCOMPARE(pointerEnterSpy.count(), 1); // should not have affected the keyboard QCOMPARE(enterSpy.count(), 1); QCOMPARE(leftSpy.count(), 0); // let's click m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerButtonPressed(Qt::LeftButton); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerButtonReleased(Qt::LeftButton); QVERIFY(enterSpy.wait()); QCOMPARE(enterSpy.count(), 2); QCOMPARE(leftSpy.count(), 1); QCOMPARE(keyboard->enteredSurface(), grandChild2Surface.data()); // click on same surface should not trigger another enter m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerButtonPressed(Qt::LeftButton); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerButtonReleased(Qt::LeftButton); QVERIFY(!enterSpy.wait(200)); QCOMPARE(enterSpy.count(), 2); QCOMPARE(leftSpy.count(), 1); QCOMPARE(keyboard->enteredSurface(), grandChild2Surface.data()); // unfocus keyboard m_seatInterface->setFocusedKeyboardSurface(nullptr); QVERIFY(leftSpy.wait()); QCOMPARE(enterSpy.count(), 2); QCOMPARE(leftSpy.count(), 2); } void TestWaylandSeat::testCursor() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy pointerSpy(m_seat, SIGNAL(hasPointerChanged(bool))); QVERIFY(pointerSpy.isValid()); m_seatInterface->setHasPointer(true); QVERIFY(pointerSpy.wait()); QSignalSpy surfaceCreatedSpy(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(surfaceCreatedSpy.isValid()); m_compositor->createSurface(m_compositor); QVERIFY(surfaceCreatedSpy.wait()); SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); QScopedPointer p(m_seat->createPointer()); QVERIFY(p->isValid()); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QSignalSpy enteredSpy(p.data(), SIGNAL(entered(quint32,QPointF))); QVERIFY(enteredSpy.isValid()); m_seatInterface->setPointerPos(QPoint(20, 18)); m_seatInterface->setFocusedPointerSurface(serverSurface, QPoint(10, 15)); quint32 serial = m_seatInterface->display()->serial(); QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.first().first().value(), serial); QVERIFY(m_seatInterface->focusedPointerSurface()); QVERIFY(m_seatInterface->focusedPointer()); QVERIFY(!m_seatInterface->focusedPointer()->cursor()); QSignalSpy cursorChangedSpy(m_seatInterface->focusedPointer(), SIGNAL(cursorChanged())); QVERIFY(cursorChangedSpy.isValid()); // just remove the pointer p->setCursor(nullptr); QVERIFY(cursorChangedSpy.wait()); QCOMPARE(cursorChangedSpy.count(), 1); auto cursor = m_seatInterface->focusedPointer()->cursor(); QVERIFY(cursor); QVERIFY(!cursor->surface()); QCOMPARE(cursor->hotspot(), QPoint()); QCOMPARE(cursor->enteredSerial(), serial); QCOMPARE(cursor->pointer(), m_seatInterface->focusedPointer()); QSignalSpy hotspotChangedSpy(cursor, SIGNAL(hotspotChanged())); QVERIFY(hotspotChangedSpy.isValid()); QSignalSpy surfaceChangedSpy(cursor, SIGNAL(surfaceChanged())); QVERIFY(surfaceChangedSpy.isValid()); QSignalSpy enteredSerialChangedSpy(cursor, SIGNAL(enteredSerialChanged())); QVERIFY(enteredSerialChangedSpy.isValid()); QSignalSpy changedSpy(cursor, SIGNAL(changed())); QVERIFY(changedSpy.isValid()); // test changing hotspot p->setCursor(nullptr, QPoint(1, 2)); QVERIFY(hotspotChangedSpy.wait()); QCOMPARE(hotspotChangedSpy.count(), 1); QCOMPARE(changedSpy.count(), 1); QCOMPARE(cursorChangedSpy.count(), 2); QCOMPARE(cursor->hotspot(), QPoint(1, 2)); QVERIFY(enteredSerialChangedSpy.isEmpty()); QVERIFY(surfaceChangedSpy.isEmpty()); // set surface auto cursorSurface = m_compositor->createSurface(m_compositor); QVERIFY(cursorSurface->isValid()); p->setCursor(cursorSurface, QPoint(1, 2)); QVERIFY(surfaceChangedSpy.wait()); QCOMPARE(surfaceChangedSpy.count(), 1); QCOMPARE(changedSpy.count(), 2); QCOMPARE(cursorChangedSpy.count(), 3); QVERIFY(enteredSerialChangedSpy.isEmpty()); QCOMPARE(cursor->hotspot(), QPoint(1, 2)); QVERIFY(cursor->surface()); // and add an image to the surface QImage img(QSize(10, 20), QImage::Format_RGB32); img.fill(Qt::red); cursorSurface->attachBuffer(m_shm->createBuffer(img)); cursorSurface->damage(QRect(0, 0, 10, 20)); cursorSurface->commit(Surface::CommitFlag::None); QVERIFY(changedSpy.wait()); QCOMPARE(changedSpy.count(), 3); QCOMPARE(cursorChangedSpy.count(), 4); QCOMPARE(surfaceChangedSpy.count(), 1); QCOMPARE(cursor->surface()->buffer()->data(), img); // and add another image to the surface QImage blue(QSize(10, 20), QImage::Format_ARGB32); blue.fill(Qt::blue); cursorSurface->attachBuffer(m_shm->createBuffer(blue)); cursorSurface->damage(QRect(0, 0, 10, 20)); cursorSurface->commit(Surface::CommitFlag::None); QVERIFY(changedSpy.wait()); QCOMPARE(changedSpy.count(), 4); QCOMPARE(cursorChangedSpy.count(), 5); QCOMPARE(cursor->surface()->buffer()->data(), blue); p->hideCursor(); QVERIFY(surfaceChangedSpy.wait()); QCOMPARE(changedSpy.count(), 5); QCOMPARE(cursorChangedSpy.count(), 6); QCOMPARE(surfaceChangedSpy.count(), 2); QVERIFY(!cursor->surface()); } void TestWaylandSeat::testCursorDamage() { // this test verifies that damaging a cursor surface triggers a cursor changed on the server using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy pointerSpy(m_seat, &Seat::hasPointerChanged); QVERIFY(pointerSpy.isValid()); m_seatInterface->setHasPointer(true); QVERIFY(pointerSpy.wait()); // create pointer QScopedPointer p(m_seat->createPointer()); QVERIFY(p->isValid()); QSignalSpy enteredSpy(p.data(), &Pointer::entered); QVERIFY(enteredSpy.isValid()); // create surface QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); m_compositor->createSurface(m_compositor); QVERIFY(surfaceCreatedSpy.wait()); SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); // send enter to the surface m_seatInterface->setFocusedPointerSurface(serverSurface); QVERIFY(enteredSpy.wait()); // create a signal spy for the cursor changed signal auto pointer = m_seatInterface->focusedPointer(); QSignalSpy cursorChangedSpy(pointer, &PointerInterface::cursorChanged); QVERIFY(cursorChangedSpy.isValid()); // now let's set the cursor Surface *cursorSurface = m_compositor->createSurface(m_compositor); QVERIFY(cursorSurface); QImage red(QSize(10, 10), QImage::Format_ARGB32); red.fill(Qt::red); cursorSurface->attachBuffer(m_shm->createBuffer(red)); cursorSurface->damage(QRect(0, 0, 10, 10)); cursorSurface->commit(Surface::CommitFlag::None); p->setCursor(cursorSurface, QPoint(0, 0)); QVERIFY(cursorChangedSpy.wait()); QCOMPARE(pointer->cursor()->surface()->buffer()->data(), red); // and damage the surface QImage blue(QSize(10, 10), QImage::Format_ARGB32); blue.fill(Qt::blue); cursorSurface->attachBuffer(m_shm->createBuffer(blue)); cursorSurface->damage(QRect(0, 0, 10, 10)); cursorSurface->commit(Surface::CommitFlag::None); QVERIFY(cursorChangedSpy.wait()); QCOMPARE(pointer->cursor()->surface()->buffer()->data(), blue); } void TestWaylandSeat::testKeyboard() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy keyboardSpy(m_seat, SIGNAL(hasKeyboardChanged(bool))); QVERIFY(keyboardSpy.isValid()); m_seatInterface->setHasKeyboard(true); QVERIFY(keyboardSpy.wait()); // create the surface QSignalSpy surfaceCreatedSpy(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(surfaceCreatedSpy.isValid()); Surface *s = m_compositor->createSurface(m_compositor); QVERIFY(surfaceCreatedSpy.wait()); SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); m_seatInterface->setFocusedKeyboardSurface(serverSurface); // no keyboard yet QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface); QVERIFY(!m_seatInterface->focusedKeyboard()); Keyboard *keyboard = m_seat->createKeyboard(m_seat); QSignalSpy repeatInfoSpy(keyboard, &Keyboard::keyRepeatChanged); QVERIFY(repeatInfoSpy.isValid()); const Keyboard &ckeyboard = *keyboard; QVERIFY(keyboard->isValid()); QCOMPARE(keyboard->isKeyRepeatEnabled(), false); QCOMPARE(keyboard->keyRepeatDelay(), 0); QCOMPARE(keyboard->keyRepeatRate(), 0); wl_display_flush(m_connection->display()); QTest::qWait(100); auto serverKeyboard = m_seatInterface->focusedKeyboard(); QVERIFY(serverKeyboard); // we should get the repeat info announced QCOMPARE(repeatInfoSpy.count(), 1); QCOMPARE(keyboard->isKeyRepeatEnabled(), false); QCOMPARE(keyboard->keyRepeatDelay(), 0); QCOMPARE(keyboard->keyRepeatRate(), 0); // let's change repeat in server m_seatInterface->setKeyRepeatInfo(25, 660); m_seatInterface->focusedKeyboard()->client()->flush(); QVERIFY(repeatInfoSpy.wait()); QCOMPARE(repeatInfoSpy.count(), 2); QCOMPARE(keyboard->isKeyRepeatEnabled(), true); QCOMPARE(keyboard->keyRepeatRate(), 25); QCOMPARE(keyboard->keyRepeatDelay(), 660); m_seatInterface->setTimestamp(1); m_seatInterface->keyPressed(KEY_K); m_seatInterface->setTimestamp(2); m_seatInterface->keyPressed(KEY_D); m_seatInterface->setTimestamp(3); m_seatInterface->keyPressed(KEY_E); QSignalSpy modifierSpy(keyboard, SIGNAL(modifiersChanged(quint32,quint32,quint32,quint32))); QVERIFY(modifierSpy.isValid()); QSignalSpy enteredSpy(keyboard, SIGNAL(entered(quint32))); QVERIFY(enteredSpy.isValid()); m_seatInterface->setFocusedKeyboardSurface(serverSurface); QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface); QCOMPARE(m_seatInterface->focusedKeyboard()->focusedSurface(), serverSurface); // we get the modifiers sent after the enter QVERIFY(modifierSpy.wait()); QCOMPARE(modifierSpy.count(), 1); QCOMPARE(modifierSpy.first().at(0).value(), quint32(0)); QCOMPARE(modifierSpy.first().at(1).value(), quint32(0)); QCOMPARE(modifierSpy.first().at(2).value(), quint32(0)); QCOMPARE(modifierSpy.first().at(3).value(), quint32(0)); QCOMPARE(enteredSpy.count(), 1); // TODO: get through API QCOMPARE(enteredSpy.first().first().value(), m_display->serial() - 1); QSignalSpy keyChangedSpy(keyboard, SIGNAL(keyChanged(quint32,KWayland::Client::Keyboard::KeyState,quint32))); QVERIFY(keyChangedSpy.isValid()); m_seatInterface->setTimestamp(4); m_seatInterface->keyReleased(KEY_E); QVERIFY(keyChangedSpy.wait()); m_seatInterface->setTimestamp(5); m_seatInterface->keyReleased(KEY_D); QVERIFY(keyChangedSpy.wait()); m_seatInterface->setTimestamp(6); m_seatInterface->keyReleased(KEY_K); QVERIFY(keyChangedSpy.wait()); m_seatInterface->setTimestamp(7); m_seatInterface->keyPressed(KEY_F1); QVERIFY(keyChangedSpy.wait()); m_seatInterface->setTimestamp(8); m_seatInterface->keyReleased(KEY_F1); QVERIFY(keyChangedSpy.wait()); QCOMPARE(keyChangedSpy.count(), 5); QCOMPARE(keyChangedSpy.at(0).at(0).value(), quint32(KEY_E)); QCOMPARE(keyChangedSpy.at(0).at(1).value(), Keyboard::KeyState::Released); QCOMPARE(keyChangedSpy.at(0).at(2).value(), quint32(4)); QCOMPARE(keyChangedSpy.at(1).at(0).value(), quint32(KEY_D)); QCOMPARE(keyChangedSpy.at(1).at(1).value(), Keyboard::KeyState::Released); QCOMPARE(keyChangedSpy.at(1).at(2).value(), quint32(5)); QCOMPARE(keyChangedSpy.at(2).at(0).value(), quint32(KEY_K)); QCOMPARE(keyChangedSpy.at(2).at(1).value(), Keyboard::KeyState::Released); QCOMPARE(keyChangedSpy.at(2).at(2).value(), quint32(6)); QCOMPARE(keyChangedSpy.at(3).at(0).value(), quint32(KEY_F1)); QCOMPARE(keyChangedSpy.at(3).at(1).value(), Keyboard::KeyState::Pressed); QCOMPARE(keyChangedSpy.at(3).at(2).value(), quint32(7)); QCOMPARE(keyChangedSpy.at(4).at(0).value(), quint32(KEY_F1)); QCOMPARE(keyChangedSpy.at(4).at(1).value(), Keyboard::KeyState::Released); QCOMPARE(keyChangedSpy.at(4).at(2).value(), quint32(8)); // releasing a key which is already released should not set a key changed m_seatInterface->keyReleased(KEY_F1); QVERIFY(!keyChangedSpy.wait(200)); // let's press it again m_seatInterface->keyPressed(KEY_F1); QVERIFY(keyChangedSpy.wait()); QCOMPARE(keyChangedSpy.count(), 6); // press again should be ignored m_seatInterface->keyPressed(KEY_F1); QVERIFY(!keyChangedSpy.wait(200)); // and release m_seatInterface->keyReleased(KEY_F1); QVERIFY(keyChangedSpy.wait()); QCOMPARE(keyChangedSpy.count(), 7); m_seatInterface->updateKeyboardModifiers(1, 2, 3, 4); QVERIFY(modifierSpy.wait()); QCOMPARE(modifierSpy.count(), 2); QCOMPARE(modifierSpy.last().at(0).value(), quint32(1)); QCOMPARE(modifierSpy.last().at(1).value(), quint32(2)); QCOMPARE(modifierSpy.last().at(2).value(), quint32(3)); QCOMPARE(modifierSpy.last().at(3).value(), quint32(4)); QSignalSpy leftSpy(keyboard, SIGNAL(left(quint32))); QVERIFY(leftSpy.isValid()); m_seatInterface->setFocusedKeyboardSurface(nullptr); QVERIFY(!m_seatInterface->focusedKeyboardSurface()); QVERIFY(!m_seatInterface->focusedKeyboard()); QVERIFY(leftSpy.wait()); QCOMPARE(leftSpy.count(), 1); // TODO: get through API QCOMPARE(leftSpy.first().first().value(), m_display->serial() -1 ); QVERIFY(!keyboard->enteredSurface()); QVERIFY(!ckeyboard.enteredSurface()); // enter it again m_seatInterface->setFocusedKeyboardSurface(serverSurface); QVERIFY(modifierSpy.wait()); QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface); QCOMPARE(m_seatInterface->focusedKeyboard()->focusedSurface(), serverSurface); QCOMPARE(enteredSpy.count(), 2); QCOMPARE(keyboard->enteredSurface(), s); QCOMPARE(ckeyboard.enteredSurface(), s); QSignalSpy serverSurfaceDestroyedSpy(serverSurface, &QObject::destroyed); QVERIFY(serverSurfaceDestroyedSpy.isValid()); + QCOMPARE(keyboard->enteredSurface(), s); delete s; - QVERIFY(serverSurfaceDestroyedSpy.wait()); + QVERIFY(!keyboard->enteredSurface()); + QVERIFY(leftSpy.wait()); + QCOMPARE(serverSurfaceDestroyedSpy.count(), 1); QVERIFY(!m_seatInterface->focusedKeyboardSurface()); QVERIFY(!m_seatInterface->focusedKeyboard()); QVERIFY(!serverKeyboard->focusedSurface()); // let's create a Surface again QScopedPointer s2(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); QCOMPARE(surfaceCreatedSpy.count(), 2); serverSurface = surfaceCreatedSpy.last().first().value(); QVERIFY(serverSurface); m_seatInterface->setFocusedKeyboardSurface(serverSurface); QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface); QCOMPARE(m_seatInterface->focusedKeyboard(), serverKeyboard); // delete the Keyboard QSignalSpy unboundSpy(serverKeyboard, &Resource::unbound); QVERIFY(unboundSpy.isValid()); QSignalSpy destroyedSpy(serverKeyboard, &Resource::destroyed); QVERIFY(destroyedSpy.isValid()); delete keyboard; QVERIFY(unboundSpy.wait()); QCOMPARE(unboundSpy.count(), 1); QCOMPARE(destroyedSpy.count(), 0); // verify that calling into the Keyboard related functionality doesn't crash m_seatInterface->setTimestamp(9); m_seatInterface->keyPressed(KEY_F2); m_seatInterface->setTimestamp(10); m_seatInterface->keyReleased(KEY_F2); m_seatInterface->setKeyRepeatInfo(30, 560); m_seatInterface->setKeyRepeatInfo(25, 660); m_seatInterface->updateKeyboardModifiers(5, 6, 7, 8); m_seatInterface->setKeymap(open("/dev/null", O_RDONLY), 0); m_seatInterface->setFocusedKeyboardSurface(nullptr); m_seatInterface->setFocusedKeyboardSurface(serverSurface); QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface); QVERIFY(!m_seatInterface->focusedKeyboard()); QVERIFY(destroyedSpy.wait()); QCOMPARE(destroyedSpy.count(), 1); // create a second Keyboard to verify that repeat info is announced properly Keyboard *keyboard2 = m_seat->createKeyboard(m_seat); QSignalSpy repeatInfoSpy2(keyboard2, &Keyboard::keyRepeatChanged); QVERIFY(repeatInfoSpy2.isValid()); QVERIFY(keyboard2->isValid()); QCOMPARE(keyboard2->isKeyRepeatEnabled(), false); QCOMPARE(keyboard2->keyRepeatDelay(), 0); QCOMPARE(keyboard2->keyRepeatRate(), 0); wl_display_flush(m_connection->display()); QVERIFY(repeatInfoSpy2.wait()); QCOMPARE(keyboard2->isKeyRepeatEnabled(), true); QCOMPARE(keyboard2->keyRepeatRate(), 25); QCOMPARE(keyboard2->keyRepeatDelay(), 660); QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface); serverKeyboard = m_seatInterface->focusedKeyboard(); QVERIFY(serverKeyboard); QSignalSpy keyboard2DestroyedSpy(serverKeyboard, &QObject::destroyed); QVERIFY(keyboard2DestroyedSpy.isValid()); delete keyboard2; QVERIFY(keyboard2DestroyedSpy.wait()); // this should have unset it on the server QVERIFY(!m_seatInterface->focusedKeyboard()); // but not the surface QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface); } void TestWaylandSeat::testCast() { using namespace KWayland::Client; Registry registry; QSignalSpy seatSpy(®istry, SIGNAL(seatAnnounced(quint32,quint32))); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(seatSpy.wait()); Seat s; QVERIFY(!s.isValid()); auto wlSeat = registry.bindSeat(seatSpy.first().first().value(), seatSpy.first().last().value()); QVERIFY(wlSeat); s.setup(wlSeat); QVERIFY(s.isValid()); QCOMPARE((wl_seat*)s, wlSeat); const Seat &s2(s); QCOMPARE((wl_seat*)s2, wlSeat); } void TestWaylandSeat::testDestroy() { using namespace KWayland::Client; QSignalSpy keyboardSpy(m_seat, SIGNAL(hasKeyboardChanged(bool))); QVERIFY(keyboardSpy.isValid()); m_seatInterface->setHasKeyboard(true); QVERIFY(keyboardSpy.wait()); Keyboard *k = m_seat->createKeyboard(m_seat); QVERIFY(k->isValid()); QSignalSpy pointerSpy(m_seat, SIGNAL(hasPointerChanged(bool))); QVERIFY(pointerSpy.isValid()); m_seatInterface->setHasPointer(true); QVERIFY(pointerSpy.wait()); Pointer *p = m_seat->createPointer(m_seat); QVERIFY(p->isValid()); QSignalSpy touchSpy(m_seat, SIGNAL(hasTouchChanged(bool))); QVERIFY(touchSpy.isValid()); m_seatInterface->setHasTouch(true); QVERIFY(touchSpy.wait()); Touch *t = m_seat->createTouch(m_seat); QVERIFY(t->isValid()); delete m_compositor; m_compositor = nullptr; connect(m_connection, &ConnectionThread::connectionDied, m_seat, &Seat::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_shm, &ShmPool::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_subCompositor, &SubCompositor::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_relativePointerManager, &RelativePointerManager::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_pointerGestures, &PointerGestures::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_queue, &EventQueue::destroy); QVERIFY(m_seat->isValid()); QSignalSpy connectionDiedSpy(m_connection, SIGNAL(connectionDied())); QVERIFY(connectionDiedSpy.isValid()); delete m_display; m_display = nullptr; m_compositorInterface = nullptr; m_seatInterface = nullptr; m_subCompositorInterface = nullptr; m_relativePointerManagerInterface = nullptr; m_pointerGesturesInterface = nullptr; QVERIFY(connectionDiedSpy.wait()); // now the seat should be destroyed; QVERIFY(!m_seat->isValid()); QVERIFY(!k->isValid()); QVERIFY(!p->isValid()); QVERIFY(!t->isValid()); // calling destroy again should not fail m_seat->destroy(); k->destroy(); p->destroy(); t->destroy(); } void TestWaylandSeat::testSelection() { using namespace KWayland::Client; using namespace KWayland::Server; QScopedPointer ddmi(m_display->createDataDeviceManager()); ddmi->create(); Registry registry; QSignalSpy dataDeviceManagerSpy(®istry, SIGNAL(dataDeviceManagerAnnounced(quint32,quint32))); QVERIFY(dataDeviceManagerSpy.isValid()); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(dataDeviceManagerSpy.wait()); QScopedPointer ddm(registry.createDataDeviceManager(dataDeviceManagerSpy.first().first().value(), dataDeviceManagerSpy.first().last().value())); QVERIFY(ddm->isValid()); QScopedPointer dd1(ddm->getDataDevice(m_seat)); QVERIFY(dd1->isValid()); QSignalSpy selectionSpy(dd1.data(), SIGNAL(selectionOffered(KWayland::Client::DataOffer*))); QVERIFY(selectionSpy.isValid()); QSignalSpy selectionClearedSpy(dd1.data(), SIGNAL(selectionCleared())); QVERIFY(selectionClearedSpy.isValid()); QSignalSpy surfaceCreatedSpy(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surface->isValid()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(!m_seatInterface->selection()); m_seatInterface->setFocusedKeyboardSurface(serverSurface); QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface); QVERIFY(!m_seatInterface->focusedKeyboard()); QVERIFY(selectionClearedSpy.wait()); QVERIFY(selectionSpy.isEmpty()); QVERIFY(!selectionClearedSpy.isEmpty()); selectionClearedSpy.clear(); QVERIFY(!m_seatInterface->selection()); // now let's try to set a selection - we have keyboard focus, so it should be sent to us QScopedPointer ds(ddm->createDataSource()); QVERIFY(ds->isValid()); ds->offer(QStringLiteral("text/plain")); dd1->setSelection(0, ds.data()); QVERIFY(selectionSpy.wait()); QCOMPARE(selectionSpy.count(), 1); auto ddi = m_seatInterface->selection(); QVERIFY(ddi); auto df = selectionSpy.first().first().value(); QCOMPARE(df->offeredMimeTypes().count(), 1); QCOMPARE(df->offeredMimeTypes().first().name(), QStringLiteral("text/plain")); // try to clear dd1->setSelection(0); QVERIFY(selectionClearedSpy.wait()); QCOMPARE(selectionClearedSpy.count(), 1); QCOMPARE(selectionSpy.count(), 1); // unset the keyboard focus m_seatInterface->setFocusedKeyboardSurface(nullptr); QVERIFY(!m_seatInterface->focusedKeyboardSurface()); QVERIFY(!m_seatInterface->focusedKeyboard()); serverSurface->client()->flush(); QCoreApplication::processEvents(); QCoreApplication::processEvents(); // try to set Selection dd1->setSelection(0, ds.data()); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCoreApplication::processEvents(); QCOMPARE(selectionSpy.count(), 1); // let's unset the selection on the seat m_seatInterface->setSelection(nullptr); // and pass focus back on our surface m_seatInterface->setFocusedKeyboardSurface(serverSurface); // we don't have a selection, so it should not send a selection QVERIFY(!selectionSpy.wait(100)); // now let's set it manually m_seatInterface->setSelection(ddi); QCOMPARE(m_seatInterface->selection(), ddi); QVERIFY(selectionSpy.wait()); QCOMPARE(selectionSpy.count(), 2); // setting the same again should not change m_seatInterface->setSelection(ddi); QVERIFY(!selectionSpy.wait(100)); // now clear it manully m_seatInterface->setSelection(nullptr); QVERIFY(selectionClearedSpy.wait()); QCOMPARE(selectionSpy.count(), 2); // create a second ddi and a data source QScopedPointer dd2(ddm->getDataDevice(m_seat)); QVERIFY(dd2->isValid()); QScopedPointer ds2(ddm->createDataSource()); QVERIFY(ds2->isValid()); ds2->offer(QStringLiteral("text/plain")); dd2->setSelection(0, ds2.data()); QVERIFY(selectionSpy.wait()); QSignalSpy cancelledSpy(ds2.data(), &DataSource::cancelled); QVERIFY(cancelledSpy.isValid()); m_seatInterface->setSelection(ddi); QVERIFY(cancelledSpy.wait()); } void TestWaylandSeat::testSelectionNoDataSource() { // this test verifies that the server doesn't crash when using setSelection with // a DataDevice which doesn't have a DataSource yet using namespace KWayland::Client; using namespace KWayland::Server; // first create the DataDevice QScopedPointer ddmi(m_display->createDataDeviceManager()); ddmi->create(); QSignalSpy ddiCreatedSpy(ddmi.data(), &DataDeviceManagerInterface::dataDeviceCreated); QVERIFY(ddiCreatedSpy.isValid()); Registry registry; QSignalSpy dataDeviceManagerSpy(®istry, &Registry::dataDeviceManagerAnnounced); QVERIFY(dataDeviceManagerSpy.isValid()); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(dataDeviceManagerSpy.wait()); QScopedPointer ddm(registry.createDataDeviceManager(dataDeviceManagerSpy.first().first().value(), dataDeviceManagerSpy.first().last().value())); QVERIFY(ddm->isValid()); QScopedPointer dd(ddm->getDataDevice(m_seat)); QVERIFY(dd->isValid()); QVERIFY(ddiCreatedSpy.wait()); auto ddi = ddiCreatedSpy.first().first().value(); QVERIFY(ddi); // now create a surface and pass it keyboard focus 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(!m_seatInterface->selection()); m_seatInterface->setFocusedKeyboardSurface(serverSurface); QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface); // now let's set the selection m_seatInterface->setSelection(ddi); } void TestWaylandSeat::testDataDeviceForKeyboardSurface() { // this test verifies that the server does not crash when creating a datadevice for the focused keyboard surface // and the currentSelection does not have a DataSource. // to properly test the functionality this test requires a second client using namespace KWayland::Client; using namespace KWayland::Server; // create the DataDeviceManager QScopedPointer ddmi(m_display->createDataDeviceManager()); ddmi->create(); QSignalSpy ddiCreatedSpy(ddmi.data(), &DataDeviceManagerInterface::dataDeviceCreated); QVERIFY(ddiCreatedSpy.isValid()); // create a second Wayland client connection to use it for setSelection auto c = new ConnectionThread; QSignalSpy connectedSpy(c, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); c->setSocketName(s_socketName); auto thread = new QThread(this); c->moveToThread(thread); thread->start(); c->initConnection(); QVERIFY(connectedSpy.wait()); QScopedPointer queue(new EventQueue); queue->setup(c); QScopedPointer registry(new Registry); QSignalSpy interfacesAnnouncedSpy(registry.data(), &Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); registry->setEventQueue(queue.data()); registry->create(c); QVERIFY(registry->isValid()); registry->setup(); QVERIFY(interfacesAnnouncedSpy.wait()); QScopedPointer seat(registry->createSeat(registry->interface(Registry::Interface::Seat).name, registry->interface(Registry::Interface::Seat).version)); QVERIFY(seat->isValid()); QScopedPointer ddm1(registry->createDataDeviceManager(registry->interface(Registry::Interface::DataDeviceManager).name, registry->interface(Registry::Interface::DataDeviceManager).version)); QVERIFY(ddm1->isValid()); // now create our first datadevice QScopedPointer dd1(ddm1->getDataDevice(seat.data())); QVERIFY(ddiCreatedSpy.wait()); auto ddi = ddiCreatedSpy.first().first().value(); QVERIFY(ddi); m_seatInterface->setSelection(ddi); // switch to other client // create a surface and pass it keyboard focus 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(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface); // now create a DataDevice Registry registry2; QSignalSpy dataDeviceManagerSpy(®istry2, &Registry::dataDeviceManagerAnnounced); QVERIFY(dataDeviceManagerSpy.isValid()); registry2.setEventQueue(m_queue); registry2.create(m_connection->display()); QVERIFY(registry2.isValid()); registry2.setup(); QVERIFY(dataDeviceManagerSpy.wait()); QScopedPointer ddm(registry2.createDataDeviceManager(dataDeviceManagerSpy.first().first().value(), dataDeviceManagerSpy.first().last().value())); QVERIFY(ddm->isValid()); QScopedPointer dd(ddm->getDataDevice(m_seat)); QVERIFY(dd->isValid()); QVERIFY(ddiCreatedSpy.wait()); // unset surface and set again m_seatInterface->setFocusedKeyboardSurface(nullptr); m_seatInterface->setFocusedKeyboardSurface(serverSurface); // and delete the connection thread again dd1.reset(); ddm1.reset(); seat.reset(); registry.reset(); queue.reset(); c->deleteLater(); thread->quit(); thread->wait(); delete thread; } void TestWaylandSeat::testTouch() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy touchSpy(m_seat, SIGNAL(hasTouchChanged(bool))); QVERIFY(touchSpy.isValid()); m_seatInterface->setHasTouch(true); QVERIFY(touchSpy.wait()); // create the surface QSignalSpy surfaceCreatedSpy(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(surfaceCreatedSpy.isValid()); Surface *s = m_compositor->createSurface(m_compositor); QVERIFY(surfaceCreatedSpy.wait()); SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); m_seatInterface->setFocusedTouchSurface(serverSurface); // no keyboard yet QCOMPARE(m_seatInterface->focusedTouchSurface(), serverSurface); QVERIFY(!m_seatInterface->focusedTouch()); QSignalSpy touchCreatedSpy(m_seatInterface, SIGNAL(touchCreated(KWayland::Server::TouchInterface*))); QVERIFY(touchCreatedSpy.isValid()); Touch *touch = m_seat->createTouch(m_seat); QVERIFY(touch->isValid()); QVERIFY(touchCreatedSpy.wait()); auto serverTouch = m_seatInterface->focusedTouch(); QVERIFY(serverTouch); QCOMPARE(touchCreatedSpy.first().first().value(), m_seatInterface->focusedTouch()); QSignalSpy sequenceStartedSpy(touch, SIGNAL(sequenceStarted(KWayland::Client::TouchPoint*))); QVERIFY(sequenceStartedSpy.isValid()); QSignalSpy sequenceEndedSpy(touch, SIGNAL(sequenceEnded())); QVERIFY(sequenceEndedSpy.isValid()); QSignalSpy sequenceCanceledSpy(touch, SIGNAL(sequenceCanceled())); QVERIFY(sequenceCanceledSpy.isValid()); QSignalSpy frameEndedSpy(touch, SIGNAL(frameEnded())); QVERIFY(frameEndedSpy.isValid()); QSignalSpy pointAddedSpy(touch, SIGNAL(pointAdded(KWayland::Client::TouchPoint*))); QVERIFY(pointAddedSpy.isValid()); QSignalSpy pointMovedSpy(touch, SIGNAL(pointMoved(KWayland::Client::TouchPoint*))); QVERIFY(pointMovedSpy.isValid()); QSignalSpy pointRemovedSpy(touch, SIGNAL(pointRemoved(KWayland::Client::TouchPoint*))); QVERIFY(pointRemovedSpy.isValid()); // try a few things m_seatInterface->setFocusedTouchSurfacePosition(QPointF(10, 20)); QCOMPARE(m_seatInterface->focusedTouchSurfacePosition(), QPointF(10, 20)); m_seatInterface->setTimestamp(1); QCOMPARE(m_seatInterface->touchDown(QPointF(15, 26)), 0); QVERIFY(sequenceStartedSpy.wait()); QCOMPARE(sequenceStartedSpy.count(), 1); QCOMPARE(sequenceEndedSpy.count(), 0); QCOMPARE(sequenceCanceledSpy.count(), 0); QCOMPARE(frameEndedSpy.count(), 0); QCOMPARE(pointAddedSpy.count(), 0); QCOMPARE(pointMovedSpy.count(), 0); QCOMPARE(pointRemovedSpy.count(), 0); TouchPoint *tp = sequenceStartedSpy.first().first().value(); QVERIFY(tp); QCOMPARE(tp->downSerial(), m_seatInterface->display()->serial()); QCOMPARE(tp->id(), 0); QVERIFY(tp->isDown()); QCOMPARE(tp->position(), QPointF(5, 6)); QCOMPARE(tp->positions().size(), 1); QCOMPARE(tp->time(), 1u); QCOMPARE(tp->timestamps().count(), 1); QCOMPARE(tp->upSerial(), 0u); QCOMPARE(tp->surface().data(), s); QCOMPARE(touch->sequence().count(), 1); QCOMPARE(touch->sequence().first(), tp); // let's end the frame m_seatInterface->touchFrame(); QVERIFY(frameEndedSpy.wait()); QCOMPARE(frameEndedSpy.count(), 1); // move the one point m_seatInterface->setTimestamp(2); m_seatInterface->touchMove(0, QPointF(10, 20)); m_seatInterface->touchFrame(); QVERIFY(frameEndedSpy.wait()); QCOMPARE(sequenceStartedSpy.count(), 1); QCOMPARE(sequenceEndedSpy.count(), 0); QCOMPARE(sequenceCanceledSpy.count(), 0); QCOMPARE(frameEndedSpy.count(), 2); QCOMPARE(pointAddedSpy.count(), 0); QCOMPARE(pointMovedSpy.count(), 1); QCOMPARE(pointRemovedSpy.count(), 0); QCOMPARE(pointMovedSpy.first().first().value(), tp); QCOMPARE(tp->id(), 0); QVERIFY(tp->isDown()); QCOMPARE(tp->position(), QPointF(0, 0)); QCOMPARE(tp->positions().size(), 2); QCOMPARE(tp->time(), 2u); QCOMPARE(tp->timestamps().count(), 2); QCOMPARE(tp->upSerial(), 0u); QCOMPARE(tp->surface().data(), s); // add onther point m_seatInterface->setTimestamp(3); QCOMPARE(m_seatInterface->touchDown(QPointF(15, 26)), 1); m_seatInterface->touchFrame(); QVERIFY(frameEndedSpy.wait()); QCOMPARE(sequenceStartedSpy.count(), 1); QCOMPARE(sequenceEndedSpy.count(), 0); QCOMPARE(sequenceCanceledSpy.count(), 0); QCOMPARE(frameEndedSpy.count(), 3); QCOMPARE(pointAddedSpy.count(), 1); QCOMPARE(pointMovedSpy.count(), 1); QCOMPARE(pointRemovedSpy.count(), 0); QCOMPARE(touch->sequence().count(), 2); QCOMPARE(touch->sequence().first(), tp); TouchPoint *tp2 = pointAddedSpy.first().first().value(); QVERIFY(tp2); QCOMPARE(touch->sequence().last(), tp2); QCOMPARE(tp2->id(), 1); QVERIFY(tp2->isDown()); QCOMPARE(tp2->position(), QPointF(5, 6)); QCOMPARE(tp2->positions().size(), 1); QCOMPARE(tp2->time(), 3u); QCOMPARE(tp2->timestamps().count(), 1); QCOMPARE(tp2->upSerial(), 0u); QCOMPARE(tp2->surface().data(), s); // send it an up m_seatInterface->setTimestamp(4); m_seatInterface->touchUp(1); m_seatInterface->touchFrame(); QVERIFY(frameEndedSpy.wait()); QCOMPARE(sequenceStartedSpy.count(), 1); QCOMPARE(sequenceEndedSpy.count(), 0); QCOMPARE(sequenceCanceledSpy.count(), 0); QCOMPARE(frameEndedSpy.count(), 4); QCOMPARE(pointAddedSpy.count(), 1); QCOMPARE(pointMovedSpy.count(), 1); QCOMPARE(pointRemovedSpy.count(), 1); QCOMPARE(pointRemovedSpy.first().first().value(), tp2); QCOMPARE(tp2->id(), 1); QVERIFY(!tp2->isDown()); QCOMPARE(tp2->position(), QPointF(5, 6)); QCOMPARE(tp2->positions().size(), 1); QCOMPARE(tp2->time(), 4u); QCOMPARE(tp2->timestamps().count(), 2); QCOMPARE(tp2->upSerial(), m_seatInterface->display()->serial()); QCOMPARE(tp2->surface().data(), s); // send another down and up m_seatInterface->setTimestamp(5); QCOMPARE(m_seatInterface->touchDown(QPointF(15, 26)), 1); m_seatInterface->touchFrame(); m_seatInterface->setTimestamp(6); m_seatInterface->touchUp(1); // and send an up for the first point m_seatInterface->touchUp(0); m_seatInterface->touchFrame(); QVERIFY(frameEndedSpy.wait()); QCOMPARE(sequenceStartedSpy.count(), 1); QCOMPARE(sequenceEndedSpy.count(), 1); QCOMPARE(sequenceCanceledSpy.count(), 0); QCOMPARE(frameEndedSpy.count(), 6); QCOMPARE(pointAddedSpy.count(), 2); QCOMPARE(pointMovedSpy.count(), 1); QCOMPARE(pointRemovedSpy.count(), 3); QCOMPARE(touch->sequence().count(), 3); QVERIFY(!touch->sequence().at(0)->isDown()); QVERIFY(!touch->sequence().at(1)->isDown()); QVERIFY(!touch->sequence().at(2)->isDown()); QVERIFY(!m_seatInterface->isTouchSequence()); // try cancel m_seatInterface->setFocusedTouchSurface(serverSurface, QPointF(15, 26)); m_seatInterface->setTimestamp(7); QCOMPARE(m_seatInterface->touchDown(QPointF(15, 26)), 0); m_seatInterface->touchFrame(); m_seatInterface->cancelTouchSequence(); QVERIFY(sequenceCanceledSpy.wait()); QCOMPARE(sequenceStartedSpy.count(), 2); QCOMPARE(sequenceEndedSpy.count(), 1); QCOMPARE(sequenceCanceledSpy.count(), 1); QCOMPARE(frameEndedSpy.count(), 7); QCOMPARE(pointAddedSpy.count(), 2); QCOMPARE(pointMovedSpy.count(), 1); QCOMPARE(pointRemovedSpy.count(), 3); QCOMPARE(touch->sequence().first()->position(), QPointF(0, 0)); // destroy touch on client side QSignalSpy unboundSpy(serverTouch, &TouchInterface::unbound); QVERIFY(unboundSpy.isValid()); QSignalSpy destroyedSpy(serverTouch, &TouchInterface::destroyed); QVERIFY(destroyedSpy.isValid()); delete touch; QVERIFY(unboundSpy.wait()); QCOMPARE(unboundSpy.count(), 1); QCOMPARE(destroyedSpy.count(), 0); QVERIFY(!serverTouch->resource()); // try to call into all the the methods of the touch interface, should not crash QCOMPARE(m_seatInterface->focusedTouch(), serverTouch); m_seatInterface->setTimestamp(8); QCOMPARE(m_seatInterface->touchDown(QPointF(15, 26)), 0); m_seatInterface->touchFrame(); m_seatInterface->touchMove(0, QPointF(0, 0)); QCOMPARE(m_seatInterface->touchDown(QPointF(15, 26)), 1); m_seatInterface->cancelTouchSequence(); QVERIFY(destroyedSpy.wait()); QCOMPARE(destroyedSpy.count(), 1); // should have unset the focused touch QVERIFY(!m_seatInterface->focusedTouch()); // but not the focused touch surface QCOMPARE(m_seatInterface->focusedTouchSurface(), serverSurface); } void TestWaylandSeat::testDisconnect() { // this test verifies that disconnecting the client cleans up correctly using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy keyboardCreatedSpy(m_seatInterface, &SeatInterface::keyboardCreated); QVERIFY(keyboardCreatedSpy.isValid()); QSignalSpy pointerCreatedSpy(m_seatInterface, &SeatInterface::pointerCreated); QVERIFY(pointerCreatedSpy.isValid()); QSignalSpy touchCreatedSpy(m_seatInterface, &SeatInterface::touchCreated); QVERIFY(touchCreatedSpy.isValid()); // create the things we need m_seatInterface->setHasKeyboard(true); m_seatInterface->setHasPointer(true); m_seatInterface->setHasTouch(true); QSignalSpy touchSpy(m_seat, &Seat::hasTouchChanged); QVERIFY(touchSpy.isValid()); QVERIFY(touchSpy.wait()); QScopedPointer keyboard(m_seat->createKeyboard()); QVERIFY(!keyboard.isNull()); QVERIFY(keyboardCreatedSpy.wait()); auto serverKeyboard = keyboardCreatedSpy.first().first().value(); QVERIFY(serverKeyboard); QScopedPointer pointer(m_seat->createPointer()); QVERIFY(!pointer.isNull()); QVERIFY(pointerCreatedSpy.wait()); auto serverPointer = pointerCreatedSpy.first().first().value(); QVERIFY(serverPointer); QScopedPointer touch(m_seat->createTouch()); QVERIFY(!touch.isNull()); QVERIFY(touchCreatedSpy.wait()); auto serverTouch = touchCreatedSpy.first().first().value(); QVERIFY(serverTouch); // setup destroys QSignalSpy keyboardDestroyedSpy(serverKeyboard, &QObject::destroyed); QVERIFY(keyboardDestroyedSpy.isValid()); QSignalSpy pointerDestroyedSpy(serverPointer, &QObject::destroyed); QVERIFY(pointerDestroyedSpy.isValid()); QSignalSpy touchDestroyedSpy(serverTouch, &QObject::destroyed); QVERIFY(touchDestroyedSpy.isValid()); QSignalSpy clientDisconnectedSpy(serverKeyboard->client(), &ClientConnection::disconnected); QVERIFY(clientDisconnectedSpy.isValid()); if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } QVERIFY(clientDisconnectedSpy.wait()); QCOMPARE(clientDisconnectedSpy.count(), 1); QCOMPARE(keyboardDestroyedSpy.count(), 0); QCOMPARE(pointerDestroyedSpy.count(), 0); QCOMPARE(touchDestroyedSpy.count(), 0); QVERIFY(keyboardDestroyedSpy.wait()); QCOMPARE(keyboardDestroyedSpy.count(), 1); QCOMPARE(pointerDestroyedSpy.count(), 1); QCOMPARE(touchDestroyedSpy.count(), 1); keyboard->destroy(); pointer->destroy(); touch->destroy(); m_relativePointerManager->destroy(); m_pointerGestures->destroy(); m_compositor->destroy(); m_seat->destroy(); m_shm->destroy(); m_subCompositor->destroy(); m_queue->destroy(); } void TestWaylandSeat::testPointerEnterOnUnboundSurface() { using namespace KWayland::Client; using namespace KWayland::Server; // create the things we need m_seatInterface->setHasKeyboard(true); m_seatInterface->setHasPointer(true); m_seatInterface->setHasTouch(true); QSignalSpy pointerChangedSpy(m_seat, &Seat::hasPointerChanged); QVERIFY(pointerChangedSpy.isValid()); QVERIFY(pointerChangedSpy.wait()); // create pointer and Surface QScopedPointer pointer(m_seat->createPointer()); QVERIFY(!pointer.isNull()); // create the surface QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer s(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); // unbind the surface again QSignalSpy surfaceUnboundSpy(serverSurface, &SurfaceInterface::unbound); QVERIFY(surfaceUnboundSpy.isValid()); s.reset(); QVERIFY(surfaceUnboundSpy.wait()); QSignalSpy clientErrorSpy(m_connection, &ConnectionThread::errorOccurred); QVERIFY(clientErrorSpy.isValid()); m_seatInterface->setFocusedPointerSurface(serverSurface); QVERIFY(!clientErrorSpy.wait(100)); } QTEST_GUILESS_MAIN(TestWaylandSeat) #include "test_wayland_seat.moc" diff --git a/autotests/server/CMakeLists.txt b/autotests/server/CMakeLists.txt index fc990d5..f5a4854 100644 --- a/autotests/server/CMakeLists.txt +++ b/autotests/server/CMakeLists.txt @@ -1,48 +1,47 @@ ######################################################## # Test WaylandServerDisplay ######################################################## -add_definitions(-DAUTOTEST_CURRENT_BIN_DIR="${CMAKE_CURRENT_BINARY_DIR}") set( testWaylandServerDisplay_SRCS test_display.cpp ) add_executable(testWaylandServerDisplay ${testWaylandServerDisplay_SRCS}) target_link_libraries( testWaylandServerDisplay Qt5::Test Qt5::Gui KF5::WaylandServer Wayland::Server) -add_test(kwayland-testWaylandServerDisplay testWaylandServerDisplay) +add_test(NAME kwayland-testWaylandServerDisplay COMMAND testWaylandServerDisplay) ecm_mark_as_test(testWaylandServerDisplay) ######################################################## # Test WaylandServerSeat ######################################################## set( testWaylandServerSeat_SRCS test_seat.cpp ) add_executable(testWaylandServerSeat ${testWaylandServerSeat_SRCS}) target_link_libraries( testWaylandServerSeat Qt5::Test Qt5::Gui KF5::WaylandServer Wayland::Server) -add_test(kwayland-testWaylandServerSeat testWaylandServerSeat) +add_test(NAME kwayland-testWaylandServerSeat COMMAND testWaylandServerSeat) ecm_mark_as_test(testWaylandServerSeat) ######################################################## # QtSurfaceExtenstion Helper ######################################################## add_executable(surfaceExtensionHelper surfaceextension_helper.cpp) target_link_libraries( surfaceExtensionHelper Qt5::Gui) ecm_mark_as_test(surfaceExtensionHelper) ######################################################## # Test QtSurfaceExtenstion ######################################################## set( testQtSurfaceExtension_SRCS test_qt_surface_extension.cpp ) add_executable(testQtSurfaceExtension ${testQtSurfaceExtension_SRCS}) target_link_libraries( testQtSurfaceExtension Qt5::Test Qt5::Gui KF5::WaylandServer) -add_test(kwayland-testQtSurfaceExtension testQtSurfaceExtension) +add_test(NAME kwayland-testQtSurfaceExtension COMMAND testQtSurfaceExtension) ecm_mark_as_test(testQtSurfaceExtension) ######################################################## # Test No XDG_RUNTIME_DIR ######################################################## add_executable(testNoXdgRuntimeDir test_no_xdg_runtime_dir.cpp) target_link_libraries( testNoXdgRuntimeDir Qt5::Test KF5::WaylandServer) -add_test(kwayland-testNoXdgRuntimeDir testNoXdgRuntimeDir) +add_test(NAME kwayland-testNoXdgRuntimeDir COMMAND testNoXdgRuntimeDir) ecm_mark_as_test(testNoXdgRuntimeDir) diff --git a/autotests/server/test_display.cpp b/autotests/server/test_display.cpp index 3421577..f9e2ee6 100644 --- a/autotests/server/test_display.cpp +++ b/autotests/server/test_display.cpp @@ -1,214 +1,220 @@ /******************************************************************** 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 // WaylandServer #include "../../src/server/display.h" #include "../../src/server/clientconnection.h" #include "../../src/server/outputmanagement_interface.h" #include "../../src/server/output_interface.h" // Wayland #include // system #include #include #include using namespace KWayland::Server; class TestWaylandServerDisplay : public QObject { Q_OBJECT private Q_SLOTS: void testSocketName(); void testStartStop(); void testAddRemoveOutput(); void testClientConnection(); void testConnectNoSocket(); void testOutputManagement(); }; void TestWaylandServerDisplay::testSocketName() { Display display; QSignalSpy changedSpy(&display, SIGNAL(socketNameChanged(QString))); QVERIFY(changedSpy.isValid()); QCOMPARE(display.socketName(), QStringLiteral("wayland-0")); const QString testSName = QStringLiteral("fooBar"); display.setSocketName(testSName); QCOMPARE(display.socketName(), testSName); QCOMPARE(changedSpy.count(), 1); QCOMPARE(changedSpy.first().first().toString(), testSName); // changing to same name again should not emit signal display.setSocketName(testSName); QCOMPARE(changedSpy.count(), 1); } void TestWaylandServerDisplay::testStartStop() { const QString testSocketName = QStringLiteral("kwin-wayland-server-display-test-0"); QDir runtimeDir(qgetenv("XDG_RUNTIME_DIR")); QVERIFY(runtimeDir.exists()); QVERIFY(!runtimeDir.exists(testSocketName)); Display display; QSignalSpy runningSpy(&display, SIGNAL(runningChanged(bool))); QVERIFY(runningSpy.isValid()); display.setSocketName(testSocketName); QVERIFY(!display.isRunning()); display.start(); // QVERIFY(runningSpy.wait()); QCOMPARE(runningSpy.count(), 1); QVERIFY(runningSpy.first().first().toBool()); QVERIFY(display.isRunning()); QVERIFY(runtimeDir.exists(testSocketName)); display.terminate(); QVERIFY(!display.isRunning()); QCOMPARE(runningSpy.count(), 2); QVERIFY(runningSpy.first().first().toBool()); QVERIFY(!runningSpy.last().first().toBool()); QVERIFY(!runtimeDir.exists(testSocketName)); } void TestWaylandServerDisplay::testAddRemoveOutput() { Display display; display.setSocketName(QStringLiteral("kwin-wayland-server-display-test-output-0")); display.start(); OutputInterface *output = display.createOutput(); QCOMPARE(display.outputs().size(), 1); QCOMPARE(display.outputs().first(), output); // create a second output OutputInterface *output2 = display.createOutput(); QCOMPARE(display.outputs().size(), 2); QCOMPARE(display.outputs().first(), output); QCOMPARE(display.outputs().last(), output2); // remove the first output display.removeOutput(output); QCOMPARE(display.outputs().size(), 1); QCOMPARE(display.outputs().first(), output2); // and delete the second delete output2; QVERIFY(display.outputs().isEmpty()); } void TestWaylandServerDisplay::testClientConnection() { Display display; display.setSocketName(QStringLiteral("kwin-wayland-server-display-test-client-connection")); display.start(); QSignalSpy connectedSpy(&display, SIGNAL(clientConnected(KWayland::Server::ClientConnection*))); QVERIFY(connectedSpy.isValid()); QSignalSpy disconnectedSpy(&display, SIGNAL(clientDisconnected(KWayland::Server::ClientConnection*))); QVERIFY(disconnectedSpy.isValid()); int sv[2]; QVERIFY(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) >= 0); auto client = wl_client_create(display, sv[0]); QVERIFY(client); QVERIFY(connectedSpy.isEmpty()); QVERIFY(display.connections().isEmpty()); ClientConnection *connection = display.getConnection(client); QVERIFY(connection); QCOMPARE(connection->client(), client); + if (getuid() == 0) { + QEXPECT_FAIL("", "Please don't run test as root", Continue); + } QVERIFY(connection->userId() != 0); + if (getgid() == 0) { + QEXPECT_FAIL("", "Please don't run test as root", Continue); + } QVERIFY(connection->groupId() != 0); QVERIFY(connection->processId() != 0); QCOMPARE(connection->display(), &display); - QCOMPARE(connection->executablePath(), QStringLiteral("%1/testWaylandServerDisplay").arg(AUTOTEST_CURRENT_BIN_DIR)); + QCOMPARE(connection->executablePath(), QCoreApplication::applicationFilePath()); QCOMPARE((wl_client*)*connection, client); const ClientConnection &constRef = *connection; QCOMPARE((wl_client*)constRef, client); QCOMPARE(connectedSpy.count(), 1); QCOMPARE(connectedSpy.first().first().value(), connection); QCOMPARE(display.connections().count(), 1); QCOMPARE(display.connections().first(), connection); QCOMPARE(connection, display.getConnection(client)); QCOMPARE(connectedSpy.count(), 1); // create a second client int sv2[2]; QVERIFY(socketpair(AF_UNIX, SOCK_STREAM, 0, sv2) >= 0); auto client2 = display.createClient(sv2[0]); QVERIFY(client2); ClientConnection *connection2 = display.getConnection(client2->client()); QVERIFY(connection2); QCOMPARE(connection2, client2); QCOMPARE(connectedSpy.count(), 2); QCOMPARE(connectedSpy.first().first().value(), connection); QCOMPARE(connectedSpy.last().first().value(), connection2); QCOMPARE(connectedSpy.last().first().value(), client2); QCOMPARE(display.connections().count(), 2); QCOMPARE(display.connections().first(), connection); QCOMPARE(display.connections().last(), connection2); QCOMPARE(display.connections().last(), client2); // and destroy QVERIFY(disconnectedSpy.isEmpty()); wl_client_destroy(client); QCOMPARE(disconnectedSpy.count(), 1); QSignalSpy clientDestroyedSpy(client2, &QObject::destroyed); QVERIFY(clientDestroyedSpy.isValid()); client2->destroy(); QVERIFY(clientDestroyedSpy.wait()); QCOMPARE(disconnectedSpy.count(), 2); close(sv[0]); close(sv[1]); close(sv2[0]); close(sv2[1]); QVERIFY(display.connections().isEmpty()); } void TestWaylandServerDisplay::testConnectNoSocket() { Display display; display.start(Display::StartMode::ConnectClientsOnly); QVERIFY(display.isRunning()); // let's try connecting a client int sv[2]; QVERIFY(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) >= 0); auto client = display.createClient(sv[0]); QVERIFY(client); wl_client_destroy(client->client()); close(sv[0]); close(sv[1]); } void TestWaylandServerDisplay::testOutputManagement() { auto display = new KWayland::Server::Display(this); display->setSocketName("kwayland-test-0"); display->start(); auto kwin = display->createOutputManagement(this); kwin->create(); QVERIFY(kwin->isValid()); } QTEST_GUILESS_MAIN(TestWaylandServerDisplay) #include "test_display.moc" diff --git a/src/client/blur.h b/src/client/blur.h index cb3898a..c76debc 100644 --- a/src/client/blur.h +++ b/src/client/blur.h @@ -1,213 +1,211 @@ /******************************************************************** Copyright 2015 Martin Gräßlin Copyright 2015 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 . *********************************************************************/ #ifndef KWAYLAND_BLUR_H #define KWAYLAND_BLUR_H #include "buffer.h" #include #include #include #include struct wl_buffer; struct wl_region; struct org_kde_kwin_blur; struct org_kde_kwin_blur_manager; class QMarginsF; class QWindow; namespace KWayland { namespace Client { class EventQueue; class Blur; class Surface; class Region; /** * TODO */ class KWAYLANDCLIENT_EXPORT BlurManager : public QObject { Q_OBJECT public: /** * Creates a new BlurManager. * Note: after constructing the BlurManager it is not yet valid and one needs * to call setup. In order to get a ready to use BlurManager prefer using * Registry::createBlurManager. **/ explicit BlurManager(QObject *parent = nullptr); virtual ~BlurManager(); /** * @returns @c true if managing a org_kde_kwin_blur_manager. **/ bool isValid() const; /** * Setup this BlurManager to manage the @p compositor. * When using Registry::createBlurManager there is no need to call this * method. **/ void setup(org_kde_kwin_blur_manager *compositor); /** * Releases the org_kde_kwin_blur_manager interface. * After the interface has been released the BlurManager instance is no * longer valid and can be setup with another org_kde_kwin_blur_manager interface. **/ void release(); /** * Destroys the data held by this BlurManager. * 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_blur_manager interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, compositor, &BlurManager::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating a Blur. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a Blur. **/ EventQueue *eventQueue(); /** * Creates and setup a new Blur with @p parent. * @param parent The parent to pass to the Blur. * @returns The new created Blur **/ Blur *createBlur(Surface *surface, QObject *parent = nullptr); void removeBlur(Surface *surface); operator org_kde_kwin_blur_manager*(); operator org_kde_kwin_blur_manager*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the BlurManager got created by * Registry::createBlurManager * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the org_kde_kwin_blur interface. * * This class is a convenient wrapper for the org_kde_kwin_blur interface. * To create a Blur call BlurManager::createBlur. * * The main purpose of this class is to setup the next frame which * should be rendered. Therefore it provides methods to add damage * and to attach a new Buffer and to finalize the frame by calling * commit. * * @see BlurManager **/ class KWAYLANDCLIENT_EXPORT Blur : public QObject { Q_OBJECT public: virtual ~Blur(); /** * Setup this Blur to manage the @p blur. * When using BlurManager::createBlur there is no need to call this * method. **/ void setup(org_kde_kwin_blur *blur); /** * Releases the org_kde_kwin_blur interface. * After the interface has been released the Blur instance is no * longer valid and can be setup with another org_kde_kwin_blur interface. **/ void release(); /** * Destroys the data held by this Blur. * 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_blur interface * once there is a new connection available. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, blur, &Blur::destroy); - * @endcode + * This method is automatically invoked when the Registry which created this + * Blur gets destroyed. * * @see release **/ void destroy(); /** * @returns @c true if managing a org_kde_kwin_blur. **/ bool isValid() const; void commit(); /** * Sets the area of the window that will have a blurred * background. * The region will have to be created with * Compositor::createRegion(QRegion) */ void setRegion(Region *region); operator org_kde_kwin_blur*(); operator org_kde_kwin_blur*() const; private: friend class BlurManager; explicit Blur(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/connection_thread.cpp b/src/client/connection_thread.cpp index 52b5097..21bb2b3 100644 --- a/src/client/connection_thread.cpp +++ b/src/client/connection_thread.cpp @@ -1,284 +1,306 @@ /******************************************************************** 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 "connection_thread.h" #include "logging_p.h" // Qt #include #include #include #include #include +#include +#include #include #include // Wayland #include namespace KWayland { namespace Client { class ConnectionThread::Private { public: Private(ConnectionThread *q); ~Private(); void doInitConnection(); void setupSocketNotifier(); void setupSocketFileWatcher(); wl_display *display = nullptr; int fd = -1; QString socketName; QDir runtimeDir; QScopedPointer socketNotifier; QScopedPointer socketWatcher; bool serverDied = false; bool foreign = false; QMetaObject::Connection eventDispatcherConnection; int error = 0; + static QVector connections; + static QMutex mutex; private: ConnectionThread *q; }; +QVector ConnectionThread::Private::connections = QVector{}; +QMutex ConnectionThread::Private::mutex{QMutex::Recursive}; + + ConnectionThread::Private::Private(ConnectionThread *q) : socketName(QString::fromUtf8(qgetenv("WAYLAND_DISPLAY"))) , runtimeDir(QString::fromUtf8(qgetenv("XDG_RUNTIME_DIR"))) , q(q) { if (socketName.isEmpty()) { socketName = QStringLiteral("wayland-0"); } + { + QMutexLocker lock(&mutex); + connections << q; + } } ConnectionThread::Private::~Private() { + { + QMutexLocker lock(&mutex); + connections.removeOne(q); + } if (display && !foreign) { wl_display_flush(display); wl_display_disconnect(display); } } void ConnectionThread::Private::doInitConnection() { if (fd != -1) { display = wl_display_connect_to_fd(fd); } else { display = wl_display_connect(socketName.toUtf8().constData()); } if (!display) { qCWarning(KWAYLAND_CLIENT) << "Failed connecting to Wayland display"; emit q->failed(); return; } if (fd != -1) { qCDebug(KWAYLAND_CLIENT) << "Connected to Wayland server over file descriptor:" << fd; } else { qCDebug(KWAYLAND_CLIENT) << "Connected to Wayland server at:" << socketName; } // setup socket notifier setupSocketNotifier(); setupSocketFileWatcher(); emit q->connected(); } void ConnectionThread::Private::setupSocketNotifier() { const int fd = wl_display_get_fd(display); socketNotifier.reset(new QSocketNotifier(fd, QSocketNotifier::Read)); QObject::connect(socketNotifier.data(), &QSocketNotifier::activated, q, [this]() { if (!display) { return; } if (wl_display_dispatch(display) == -1) { error = wl_display_get_error(display); if (error != 0) { if (display) { free(display); display = nullptr; } emit q->errorOccurred(); return; } } emit q->eventsRead(); } ); } void ConnectionThread::Private::setupSocketFileWatcher() { if (!runtimeDir.exists() || fd != -1) { return; } socketWatcher.reset(new QFileSystemWatcher); socketWatcher->addPath(runtimeDir.absoluteFilePath(socketName)); QObject::connect(socketWatcher.data(), &QFileSystemWatcher::fileChanged, q, [this] (const QString &file) { if (QFile::exists(file) || serverDied) { return; } qCWarning(KWAYLAND_CLIENT) << "Connection to server went away"; serverDied = true; if (display) { free(display); display = nullptr; } socketNotifier.reset(); // need a new filesystem watcher socketWatcher.reset(new QFileSystemWatcher); socketWatcher->addPath(runtimeDir.absolutePath()); QObject::connect(socketWatcher.data(), &QFileSystemWatcher::directoryChanged, q, [this]() { if (!serverDied) { return; } if (runtimeDir.exists(socketName)) { qCDebug(KWAYLAND_CLIENT) << "Socket reappeared"; socketWatcher.reset(); serverDied = false; error = 0; q->initConnection(); } } ); emit q->connectionDied(); } ); } ConnectionThread::ConnectionThread(QObject *parent) : QObject(parent) , d(new Private(this)) { d->eventDispatcherConnection = connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, [this] { if (d->display) { wl_display_flush(d->display); } }, Qt::DirectConnection); } ConnectionThread::~ConnectionThread() { disconnect(d->eventDispatcherConnection); } ConnectionThread *ConnectionThread::fromApplication(QObject *parent) { if (!QGuiApplication::platformName().contains(QStringLiteral("wayland"), Qt::CaseInsensitive)) { return nullptr; } QPlatformNativeInterface *native = qApp->platformNativeInterface(); if (!native) { return nullptr; } wl_display *display = reinterpret_cast(native->nativeResourceForIntegration(QByteArrayLiteral("wl_display"))); if (!display) { return nullptr; } ConnectionThread *ct = new ConnectionThread(parent); ct->d->foreign = true; ct->d->display = display; + connect(native, &QObject::destroyed, ct, &ConnectionThread::connectionDied); return ct; } void ConnectionThread::initConnection() { QMetaObject::invokeMethod(this, "doInitConnection", Qt::QueuedConnection); } void ConnectionThread::doInitConnection() { d->doInitConnection(); } void ConnectionThread::setSocketName(const QString &socketName) { if (d->display) { // already initialized return; } d->socketName = socketName; } void ConnectionThread::setSocketFd(int fd) { if (d->display) { // already initialized return; } d->fd = fd; } wl_display *ConnectionThread::display() { return d->display; } QString ConnectionThread::socketName() const { return d->socketName; } void ConnectionThread::flush() { if (!d->display) { return; } wl_display_flush(d->display); } void ConnectionThread::roundtrip() { if (!d->display) { return; } if (d->foreign) { // try to perform roundtrip through the QPA plugin if it's supported if (QPlatformNativeInterface *native = qApp->platformNativeInterface()) { // in case the platform provides a dedicated roundtrip function use that install of wl_display_roundtrip QFunctionPointer roundtripFunction = native->platformFunction(QByteArrayLiteral("roundtrip")); if (roundtripFunction) { roundtripFunction(); return; } } } wl_display_roundtrip(d->display); } bool ConnectionThread::hasError() const { return d->error != 0; } int ConnectionThread::errorCode() const { return d->error; } +QVector ConnectionThread::connections() +{ + return Private::connections; +} + } } diff --git a/src/client/connection_thread.h b/src/client/connection_thread.h index 7c8d33f..0c4438a 100644 --- a/src/client/connection_thread.h +++ b/src/client/connection_thread.h @@ -1,263 +1,270 @@ /******************************************************************** 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_CONNECTION_THREAD_H #define WAYLAND_CONNECTION_THREAD_H #include +#include #include struct wl_display; namespace KWayland { /** * @short KWayland Client. * * This namespace groups all classes related to the Client module. * * The main entry point into the KWayland::Client API is the ConnectionThread class. * It allows to create a Wayland client connection either in a native way or wrap a * connection created by the QtWayland QPA plugin. * * KWayland::Client provides one the one hand a low-level API to interact with the * Wayland API, on the other hand an easy to use convenience API. Each class directly * relates to a low-level Wayland type and allows direct casting into the type. * * On the convenience side KWayland::Client allows easy creation of objects, signals * emitted for Wayland events and easy conversion from Qt to Wayland types. * * Once one has a ConnectionThread created, it's possible to setup a Registry to * get a listing of all registered globals. For each global the Registry provides a convenience * method to create the resource. * * @see ConnectionThread * @see Registry **/ namespace Client { /** * @short Creates and manages the connection to a Wayland server. * * The purpose of this class is to create the connection to a Wayland server * and to manage it. As the name suggests it's intended to move instances of * this class into a dedicated thread. This also means that this class doesn't * inherit QThread. In order to use it in a threaded way one needs to create a * QThread and move the object there: * * @code * ConnectionThread *connection = new ConnectionThread; * QThread *thread = new QThread; * connection->moveToThread(thread); * thread->start(); * @endcode * * To finalize the initialization of the connection one needs to call @link ::initConnection @endlink. * This starts an asynchronous connection initialization. In case the initialization * succeeds the signal @link ::connected @endlink will be emitted, otherwise @link ::failed @endlink will * be emitted: * * @code * connect(connection, &ConnectionThread::connected, [connection] { * qDebug() << "Successfully connected to Wayland server at socket:" << connection->socketName(); * }); * connect(connection, &ConnectionThread::failed, [connection] { * qDebug() << "Failed to connect to Wayland server at socket:" << connection->socketName(); * }); * connection->initConnection(); * @endcode * * This class is also responsible for dispatching events. Whenever new data is available on * the Wayland socket, it will be dispatched and the signal @link ::eventsRead @endlink is emitted. * This allows further event queues in other threads to also dispatch their events. * * Furthermore this class flushes the Wayland connection whenever the QAbstractEventDispatcher * is about to block. * * To disconnect the connection to the Wayland server one should delete the instance of this * class and quit the dedicated thread: * * @code * connection->deleteLater(); * thread->quit(); * thread->wait(); * @endcode * * In addition the ConnectionThread provides integration with QtWayland QPA plugin. For that * it provides a static factory method: * * @code * auto connection = ConnectionThread::fromApplication(); * @endcode * * The semantics of the ConnectionThread are slightly changed if it's integrated with QtWayland. * The ConnectionThread does not hold the connection, does not emit connected or released signals * (one can safely assume that the connection is valid when integrating with the Qt application), * does not dispatch events. Given that the use case of the ConnectionThread is rather limited to * a convenient API around wl_display to allow easily setup an own Registry in a QtWayland powered * application. Also moving the ConnectionThread to a different thread is not necessarily recommended * in that case as QtWayland holds it's connection in an own thread anyway. * **/ class KWAYLANDCLIENT_EXPORT ConnectionThread : public QObject { Q_OBJECT public: explicit ConnectionThread(QObject *parent = nullptr); virtual ~ConnectionThread(); /** * Creates a ConnectionThread for the used QGuiApplication. * This is an integration feature for QtWayland. On non-wayland platforms this method returns * @c nullptr. * * The returned ConnectionThread will be fully setup, which means it manages a wl_display. * There is no need to initConnection and the connected or failed signals won't be emitted. * When the created ConnectionThread gets destroyed the managed wl_display won't be disconnected * as that's managed by Qt. * * The returned ConnectionThread is not able to detect (protocol) error. The signal * {@link errorOccurred} won't be emitted, {@link hasError} will return @c false, even if the * actual connection held by QtWayland is on error. The behavior of QtWayland is to exit the * application on error. * * @since 5.4 **/ static ConnectionThread *fromApplication(QObject *parent = nullptr); /** * The display this ConnectionThread is connected to. * As long as there is no connection this method returns @c null. * @see initConnection **/ wl_display *display(); /** * @returns the name of the socket it connects to. **/ QString socketName() const; /** * Sets the @p socketName to connect to. * Only applies if called before calling initConnection. * The default socket name is derived from environment variable WAYLAND_DISPLAY * and if not set is hard coded to "wayland-0". * * The socket name will be ignored if a file descriptor has been set through @link setSocketFd @endlink. * * @see setSocketFd **/ void setSocketName(const QString &socketName); /** * Sets the socket @p fd to connect to. * Only applies if called before calling initConnection. * If this method is invoked, the connection will be created on the file descriptor * and not on the socket name passed through @link setSocketName @endlink or through the * default environment variable WAYLAND_DISPLAY. * @see setSocketName **/ void setSocketFd(int fd); /** * Trigger a blocking roundtrip to the Wayland server. Ensures that all events are processed * before returning to the event loop. * * @since 5.4 **/ void roundtrip(); /** * @returns whether the Wayland connection experienced an error * @see errorCode * @see errorOccurred * @since 5.23 **/ bool hasError() const; /** * @returns the error code of the last occurred error or @c 0 if the connection doesn't have an error * @see hasError * @see errorOccurred * @since 5.23 **/ int errorCode() const; + /** + * @returns all connections created in this application + * @since 5.37 + **/ + static QVector connections(); + public Q_SLOTS: /** * Initializes the connection in an asynchronous way. * In case the connection gets established the signal @link ::connected @endlink will be * emitted, on failure the signal @link ::failed @endlink will be emitted. * * @see connected * @see failed **/ void initConnection(); /** * Explicitly flush the Wayland display. * @since 5.3 **/ void flush(); Q_SIGNALS: /** * Emitted once a connection to a Wayland server is established. * Normally emitted after invoking initConnection(), but might also be * emitted after re-connecting to another server. **/ void connected(); /** * Emitted if connecting to a Wayland server failed. **/ void failed(); /** * Emitted whenever new events are ready to be read. **/ void eventsRead(); /** * Emitted if the Wayland server connection dies. * If the socket reappears, it is tried to reconnect. **/ void connectionDied(); /** * The Wayland connection experienced a fatal error. * The ConnectionThread is no longer valid, no requests may be sent. * This has the same effects as {@link connectionDied}. * * @see hasError * @see errorCode * @since 5.23 **/ void errorOccurred(); private Q_SLOTS: /** * @internal **/ void doInitConnection(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/contrast.h b/src/client/contrast.h index 178fb7f..cc32a53 100644 --- a/src/client/contrast.h +++ b/src/client/contrast.h @@ -1,209 +1,207 @@ /******************************************************************** Copyright 2015 Martin Gräßlin Copyright 2015 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 . *********************************************************************/ #ifndef KWAYLAND_CONTRAST_H #define KWAYLAND_CONTRAST_H #include #include #include #include struct org_kde_kwin_contrast; struct org_kde_kwin_contrast_manager; namespace KWayland { namespace Client { class EventQueue; class Contrast; class Surface; class Region; /** * TODO */ class KWAYLANDCLIENT_EXPORT ContrastManager : public QObject { Q_OBJECT public: /** * Creates a new ContrastManager. * Note: after constructing the ContrastManager it is not yet valid and one needs * to call setup. In order to get a ready to use ContrastManager prefer using * Registry::createContrastManager. **/ explicit ContrastManager(QObject *parent = nullptr); virtual ~ContrastManager(); /** * @returns @c true if managing a org_kde_kwin_contrast_manager. **/ bool isValid() const; /** * Setup this ContrastManager to manage the @p contrastManager. * When using Registry::createContrastManager there is no need to call this * method. **/ void setup(org_kde_kwin_contrast_manager *contrastManager); /** * Releases the org_kde_kwin_contrast_manager interface. * After the interface has been released the ContrastManager instance is no * longer valid and can be setup with another org_kde_kwin_contrast_manager interface. **/ void release(); /** * Destroys the data held by this ContrastManager. * 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_contrast_manager interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, contrastManager, &ContrastManager::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating a Contrast. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a Contrast. **/ EventQueue *eventQueue(); /** * Creates and setup a new Contrast with @p parent. * @param parent The parent to pass to the Contrast. * @returns The new created Contrast **/ Contrast *createContrast(Surface *surface, QObject *parent = nullptr); void removeContrast(Surface *surface); operator org_kde_kwin_contrast_manager*(); operator org_kde_kwin_contrast_manager*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the ContrastManager got created by * Registry::createContrastManager * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the org_kde_kwin_contrast interface. * * This class is a convenient wrapper for the org_kde_kwin_contrast interface. * To create a Contrast call ContrastManager::createContrast. * * The main purpose of this class is to setup the next frame which * should be rendered. Therefore it provides methods to add damage * and to attach a new Buffer and to finalize the frame by calling * commit. * * @see ContrastManager **/ class KWAYLANDCLIENT_EXPORT Contrast : public QObject { Q_OBJECT public: virtual ~Contrast(); /** * Setup this Contrast to manage the @p contrast. * When using ContrastManager::createContrast there is no need to call this * method. **/ void setup(org_kde_kwin_contrast *contrast); /** * Releases the org_kde_kwin_contrast interface. * After the interface has been released the Contrast instance is no * longer valid and can be setup with another org_kde_kwin_contrast interface. **/ void release(); /** * Destroys the data held by this Contrast. * 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_contrast interface * once there is a new connection available. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, contrast, &Contrast::destroy); - * @endcode + * This method is automatically invoked when the Registry which created this + * Contrast gets destroyed. * * @see release **/ void destroy(); /** * @returns @c true if managing a org_kde_kwin_contrast. **/ bool isValid() const; void commit(); /** * Sets the area of the window that will have a contrastred * background. * The region will have to be created with * Compositor::createRegion(QRegion) */ void setRegion(Region *region); void setContrast(qreal contrast); void setIntensity(qreal intensity); void setSaturation(qreal saturation); operator org_kde_kwin_contrast*(); operator org_kde_kwin_contrast*() const; private: friend class ContrastManager; explicit Contrast(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/datadevice.h b/src/client/datadevice.h index 1dbb0b0..f5b6247 100644 --- a/src/client/datadevice.h +++ b/src/client/datadevice.h @@ -1,160 +1,158 @@ /******************************************************************** 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_DATADEVICE_H #define WAYLAND_DATADEVICE_H #include "dataoffer.h" #include #include struct wl_data_device; namespace KWayland { namespace Client { class DataSource; class Surface; /** * @short Wrapper for the wl_data_device interface. * * This class is a convenient wrapper for the wl_data_device interface. * To create a DataDevice call DataDeviceManager::getDataDevice. * * @see DataDeviceManager **/ class KWAYLANDCLIENT_EXPORT DataDevice : public QObject { Q_OBJECT public: explicit DataDevice(QObject *parent = nullptr); virtual ~DataDevice(); /** * Setup this DataDevice to manage the @p dataDevice. * When using DataDeviceManager::createDataDevice there is no need to call this * method. **/ void setup(wl_data_device *dataDevice); /** * Releases the wl_data_device interface. * After the interface has been released the DataDevice instance is no * longer valid and can be setup with another wl_data_device interface. **/ void release(); /** * Destroys the data held by this DataDevice. * 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 interface * once there is a new connection available. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, source, &DataDevice::destroy); - * @endcode + * This method is automatically invoked when the Registry which created this + * DataDevice gets destroyed. * * @see release **/ void destroy(); /** * @returns @c true if managing a wl_data_device. **/ bool isValid() const; void startDrag(quint32 serial, DataSource *source, Surface *origin, Surface *icon = nullptr); void startDragInternally(quint32 serial, Surface *origin, Surface *icon = nullptr); void setSelection(quint32 serial, DataSource *source = nullptr); void clearSelection(quint32 serial); DataOffer *offeredSelection() const; /** * @returns the currently focused surface during drag'n'drop on this DataDevice. * @since 5.22 **/ QPointer dragSurface() const; /** * @returns the DataOffer during a drag'n'drop operation. * @since 5.22 **/ DataOffer *dragOffer() const; operator wl_data_device*(); operator wl_data_device*() const; Q_SIGNALS: void selectionOffered(KWayland::Client::DataOffer*); void selectionCleared(); /** * Notification that a drag'n'drop operation entered a Surface on this DataDevice. * * @param serial The serial for this enter * @param relativeToSurface Coordinates relative to the upper-left corner of the Surface. * @see dragSurface * @see dragOffer * @see dragLeft * @see dragMotion * @since 5.22 **/ void dragEntered(quint32 serial, const QPointF &relativeToSurface); /** * Notification that the drag'n'drop operation left the Surface on this DataDevice. * * The leave notification is sent before the enter notification for the new focus. * @see dragEnter * @since 5.22 **/ void dragLeft(); /** * Notification of drag motion events on the current drag surface. * * @param relativeToSurface Coordinates relative to the upper-left corner of the entered Surface. * @param time timestamp with millisecond granularity * @see dragEntered * @since 5.22 **/ void dragMotion(const QPointF &relativeToSurface, quint32 time); /** * Emitted when the implicit grab is removed and the drag'n'drop operation ended on this * DataDevice. * * The client can now start a data transfer on the DataOffer. * @see dragEntered * @see dragOffer * @since 5.22 **/ void dropped(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/datadevicemanager.h b/src/client/datadevicemanager.h index bbe7a59..d899840 100644 --- a/src/client/datadevicemanager.h +++ b/src/client/datadevicemanager.h @@ -1,144 +1,142 @@ /******************************************************************** 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: /** * 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. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, manager, &DataDeviceManager::destroy); - * @endcode + * 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; }; } } #endif diff --git a/src/client/dataoffer.h b/src/client/dataoffer.h index 03a990c..edcebd3 100644 --- a/src/client/dataoffer.h +++ b/src/client/dataoffer.h @@ -1,102 +1,100 @@ /******************************************************************** 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 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. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, source, &DataOffer::destroy); - * @endcode + * 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); operator wl_data_offer*(); operator wl_data_offer*() const; Q_SIGNALS: void mimeTypeOffered(const QString&); 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.h b/src/client/datasource.h index fb5a50d..66fb005 100644 --- a/src/client/datasource.h +++ b/src/client/datasource.h @@ -1,119 +1,117 @@ /******************************************************************** 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 #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. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, source, &DataSource::destroy); - * @endcode + * 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); 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(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/dpms.h b/src/client/dpms.h index ee599cc..0a76145 100644 --- a/src/client/dpms.h +++ b/src/client/dpms.h @@ -1,248 +1,246 @@ /******************************************************************** 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_CLIENT_DPMS_H #define KWAYLAND_CLIENT_DPMS_H #include #include struct org_kde_kwin_dpms; struct org_kde_kwin_dpms_manager; namespace KWayland { namespace Client { class EventQueue; class Dpms; class Output; /** * @short Wrapper for the org_kde_kwin_dpms_manager interface. * * This class provides a convenient wrapper for the org_kde_kwin_dpms_manager interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the DpmsManager interface: * @code * DpmsManager *m = registry->createDpmsManager(name, version); * @endcode * * This creates the DpmsManager and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * DpmsManager *m = new DpmsManager; * m->setup(registry->bindDpmsManager(name, version)); * @endcode * * The DpmsManager can be used as a drop-in replacement for any org_kde_kwin_dpms_manager * pointer as it provides matching cast operators. * * @see Registry * @since 5.5 **/ class KWAYLANDCLIENT_EXPORT DpmsManager : public QObject { Q_OBJECT public: /** * Creates a new DpmsManager. * Note: after constructing the DpmsManager it is not yet valid and one needs * to call setup. In order to get a ready to use DpmsManager prefer using * Registry::createDpmsManager. **/ explicit DpmsManager(QObject *parent = nullptr); virtual ~DpmsManager(); /** * @returns @c true if managing a org_kde_kwin_dpms_manager. **/ bool isValid() const; /** * Setup this DpmsManager to manage the @p manager. * When using Registry::createDpmsManager there is no need to call this * method. **/ void setup(org_kde_kwin_dpms_manager *manager); /** * Releases the org_kde_kwin_dpms_manager interface. * After the interface has been released the DpmsManager instance is no * longer valid and can be setup with another org_kde_kwin_dpms_manager interface. **/ void release(); /** * Destroys the data held by this DpmsManager. * 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_dpms_manager interface * once there is a new connection available. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, manager, &DpmsManager::destroy); - * @endcode + * This method is automatically invoked when the Registry which created this + * DPMS gets destroyed. * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating a Dpms. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a Dpms. **/ EventQueue *eventQueue(); Dpms *getDpms(Output *output, QObject *parent = nullptr); operator org_kde_kwin_dpms_manager*(); operator org_kde_kwin_dpms_manager*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the DpmsManager got created by * Registry::createDpmsManager **/ void removed(); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the org_kde_kwin_dpms interface. * * This class is a convenient wrapper for the org_kde_kwin_dpms interface. * To create a Dpms call DpmsManager::getDpms. * * @see DpmsManager **/ class KWAYLANDCLIENT_EXPORT Dpms : public QObject { Q_OBJECT public: virtual ~Dpms(); enum class Mode { On, Standby, Suspend, Off }; /** * Setup this Dpms to manage the @p dpms. * When using DpmsManager::createDpms there is no need to call this * method. **/ void setup(org_kde_kwin_dpms *dpms); /** * Releases the org_kde_kwin_dpms interface. * After the interface has been released the Dpms instance is no * longer valid and can be setup with another org_kde_kwin_dpms interface. **/ void release(); /** * Destroys the data held by this Dpms. * 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_dpms interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, source, &Dpms::destroy); * @endcode * * @see release **/ void destroy(); /** * @returns @c true if managing a org_kde_kwin_dpms. **/ bool isValid() const; /** * @returns the Output for which this Dpms got created **/ QPointer output() const; /** * Whether Dpms is supported for the Output. * Initially set to @c false. * @returns whether Dpms is supported for the Output. * @see supportedChanged **/ bool isSupported() const; /** * The current Dpms mode. * Initially set to @c Mode::On. * @returns the current Dpms mode of the Output * @see modeChanged **/ Mode mode() const; /** * Request to change the Output into Dpms @p mode. * The Wayland compositor is not obliged to honor the request. * If the mode changes the client is notified and @link modeChanged @endlink gets emitted. * @param mode The requested Dpms mode. **/ void requestMode(Mode mode); operator org_kde_kwin_dpms*(); operator org_kde_kwin_dpms*() const; Q_SIGNALS: /** * Emitted if the supported state on the Output changes. * @see isSupported **/ void supportedChanged(); /** * Emitted if the Dpms mode on the Output changes. * @see mode **/ void modeChanged(); private: friend class DpmsManager; explicit Dpms(const QPointer &o, QObject *parent = nullptr); class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::Dpms::Mode) #endif diff --git a/src/client/event_queue.h b/src/client/event_queue.h index d694b34..cc23b5f 100644 --- a/src/client/event_queue.h +++ b/src/client/event_queue.h @@ -1,169 +1,164 @@ /******************************************************************** 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_EVENT_QUEUE_H #define WAYLAND_EVENT_QUEUE_H #include #include struct wl_display; struct wl_proxy; struct wl_event_queue; namespace KWayland { namespace Client { class ConnectionThread; /** * @short Wrapper class for wl_event_queue interface. * * The EventQueue is needed if a different thread is used for the connection. * If the interface wrappers are held in a different thread than the connection thread * an EventQueue is needed for the thread which holds the interface wrappers. A common * example is a dedicated connection thread while the interface wrappers are created * in the main thread. * * All interface wrappers are set up to support the EventQueue in the most convenient * way. The EventQueue needs only to be passed to the Registry. The EventQueue will then * be passed to all created wrappers through the tree. * * @code * ConnectionThread connection; * EventQueue queue; * Registry registry; * * connect(&connection, &ConnectionThread::connected, this, [&] { * queue.setup(&connection); * registry.setEventQueue(&queue); * registry.setup(&connection); * registry.create(); * }); * * connection.initConnection(); * @endcode * * The EventQueue can be used as a drop-in replacement for any wl_event_queue * pointer as it provides matching cast operators. **/ class KWAYLANDCLIENT_EXPORT EventQueue : public QObject { Q_OBJECT public: explicit EventQueue(QObject *parent = nullptr); virtual ~EventQueue(); /** * Creates the event queue for the @p display. * * Note: this will not automatically setup the dispatcher. * When using this method one needs to ensure that dispatch * gets invoked whenever new events need to be dispatched. * @see dispatch **/ void setup(wl_display *display); /** * Creates the event queue for the @p connection. * * This method also connects the eventsRead signal of the ConnectionThread * to the dispatch method. Events will be automatically dispatched without * the need to call dispatch manually. * @see dispatch **/ void setup(ConnectionThread *connection); /** * @returns @c true if EventQueue is setup. **/ bool isValid(); /** * Releases the wl_event_queue interface. * After the interface has been released the EventQueue instance is no * longer valid and can be setup with another wl_event_queue interface. **/ void release(); /** * Destroys the data held by this EventQueue. * 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_event_queue interface * once there is a new connection available. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, queue, &EventQueue::destroy); - * @endcode - * * @see release **/ void destroy(); /** * Adds the @p proxy to the EventQueue. **/ void addProxy(wl_proxy *proxy); /** * Adds the @p proxy of type wl_interface (e.g. wl_compositor) to the EventQueue. **/ template void addProxy(wl_interface *proxy); /** * Adds the @p proxy wrapper class of type T referencing the wl_interface to the EventQueue. **/ template void addProxy(T *proxy); operator wl_event_queue*(); operator wl_event_queue*() const; public Q_SLOTS: /** * Dispatches all pending events on the EventQueue. **/ void dispatch(); private: class Private; QScopedPointer d; }; template inline void EventQueue::addProxy(wl_interface *proxy) { addProxy(reinterpret_cast(proxy)); } template inline void EventQueue::addProxy(T *proxy) { addProxy(reinterpret_cast((wl_interface*)*(proxy))); } } } #endif diff --git a/src/client/fakeinput.h b/src/client/fakeinput.h index 9498822..d7624bd 100644 --- a/src/client/fakeinput.h +++ b/src/client/fakeinput.h @@ -1,231 +1,229 @@ /******************************************************************** 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_FAKEINPUT_H #define KWAYLAND_FAKEINPUT_H #include #include struct org_kde_kwin_fake_input; namespace KWayland { namespace Client { class EventQueue; class FakeInputTimeout; class Seat; /** * @short Wrapper for the org_kde_kwin_fake_input interface. * * FakeInput allows to fake input events into the Wayland server. This is a privileged * Wayland interface and the Wayland server is allowed to ignore all events. * * This class provides a convenient wrapper for the org_kde_kwin_fake_input interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the FakeInput interface: * @code * FakeInput *m = registry->createFakeInput(name, version); * @endcode * * This creates the FakeInput and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * FakeInput *m = new FakeInput; * m->setup(registry->bindFakeInput(name, version)); * @endcode * * The FakeInput can be used as a drop-in replacement for any org_kde_kwin_fake_input * pointer as it provides matching cast operators. * * @see Registry **/ class KWAYLANDCLIENT_EXPORT FakeInput : public QObject { Q_OBJECT public: /** * Creates a new FakeInput. * Note: after constructing the FakeInput it is not yet valid and one needs * to call setup. In order to get a ready to use FakeInput prefer using * Registry::createFakeInput. **/ explicit FakeInput(QObject *parent = nullptr); virtual ~FakeInput(); /** * @returns @c true if managing a org_kde_kwin_fake_input. **/ bool isValid() const; /** * Setup this FakeInput to manage the @p manager. * When using Registry::createFakeInput there is no need to call this * method. **/ void setup(org_kde_kwin_fake_input *manager); /** * Releases the org_kde_kwin_fake_input interface. * After the interface has been released the FakeInput instance is no * longer valid and can be setup with another org_kde_kwin_fake_input interface. **/ void release(); /** * Destroys the data held by this FakeInput. * 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_fake_input interface * once there is a new connection available. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, manager, &FakeInput::destroy); - * @endcode + * This method is automatically invoked when the Registry which created this + * FakeInput gets destroyed. * * @see release **/ void destroy(); /** * Sets the @p queue to use for bound proxies. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for bound proxies. **/ EventQueue *eventQueue(); /** * Authenticate with the Wayland server in order to request sending fake input events. * The Wayland server might ignore all requests without a prior authentication. * * The Wayland server might use the provided @p applicationName and @p reason to ask * the user whether this request should get authenticated. * * There is no way for the client to figure out whether the authentication was granted * or denied. The client should assume that it wasn't granted. * * @param applicationName A human readable description of the application * @param reason A human readable explanation why this application wants to send fake requests **/ void authenticate(const QString &applicationName, const QString &reason); /** * Request a relative pointer motion of @p delta pixels. **/ void requestPointerMove(const QSizeF &delta); /** * Convenience overload. * @see requestPointerButtonPress(quint32) **/ void requestPointerButtonPress(Qt::MouseButton button); /** * Request a pointer button press. * @param linuxButton The button code as defined in linux/input-event-codes.h **/ void requestPointerButtonPress(quint32 linuxButton); /** * Convenience overload. * @see requestPointerButtonRelease(quint32) **/ void requestPointerButtonRelease(Qt::MouseButton button); /** * Request a pointer button release. * @param linuxButton The button code as defined in linux/input-event-codes.h **/ void requestPointerButtonRelease(quint32 linuxButton); /** * Convenience overload. * @see requestPointerButtonClick(quint32) **/ void requestPointerButtonClick(Qt::MouseButton button); /** * Requests a pointer button click, that is a press directly followed by a release. * @param linuxButton The button code as defined in linux/input-event-codes.h **/ void requestPointerButtonClick(quint32 linuxButton); /** * Request a scroll of the pointer @p axis with @p delta. **/ void requestPointerAxis(Qt::Orientation axis, qreal delta); /** * Request a touch down at @p pos in global coordinates. * * If this is the first touch down it starts a touch sequence. * @param id The id to identify the touch point * @param pos The global position of the touch point * @see requestTouchMotion * @see requestTouchUp * @since 5.23 **/ void requestTouchDown(quint32 id, const QPointF &pos); /** * Request a move of the touch point identified by @p id to new global @p pos. * @param id The id to identify the touch point * @param pos The global position of the touch point * @see requestTouchDown * @since 5.23 **/ void requestTouchMotion(quint32 id, const QPointF &pos); /** * Requests a touch up of the touch point identified by @p id. * @param id The id to identify the touch point * @since 5.23 **/ void requestTouchUp(quint32 id); /** * Requests to cancel the current touch event sequence. * @since 5.23 **/ void requestTouchCancel(); /** * Requests a touch frame. This allows to manipulate multiple touch points in one * event and notify that the set of touch events for the current frame are finished. * @since 5.23 **/ void requestTouchFrame(); operator org_kde_kwin_fake_input*(); operator org_kde_kwin_fake_input*() 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::createFakeInput * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/idle.h b/src/client/idle.h index 64baa23..f34c947 100644 --- a/src/client/idle.h +++ b/src/client/idle.h @@ -1,241 +1,239 @@ /******************************************************************** 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_IDLE_H #define KWAYLAND_IDLE_H #include #include struct org_kde_kwin_idle; struct org_kde_kwin_idle_timeout; namespace KWayland { namespace Client { class EventQueue; class IdleTimeout; class Seat; /** * @short Wrapper for the org_kde_kwin_idle interface. * * With the help of Idle it is possible to get notified when a Seat is not being * used. E.g. a chat application which wants to set the user automatically to away * if the user did not interact with the Seat for 5 minutes can create an IdleTimeout * to get notified when the Seat has been idle for the given amount of time. * * This class provides a convenient wrapper for the org_kde_kwin_idle interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the Idle interface: * @code * Idle *m = registry->createIdle(name, version); * @endcode * * This creates the Idle and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * Idle *m = new Idle; * m->setup(registry->bindIdle(name, version)); * @endcode * * The Idle can be used as a drop-in replacement for any org_kde_kwin_idle * pointer as it provides matching cast operators. * * @see Registry **/ class KWAYLANDCLIENT_EXPORT Idle : public QObject { Q_OBJECT public: /** * Creates a new Idle. * Note: after constructing the Idle it is not yet valid and one needs * to call setup. In order to get a ready to use Idle prefer using * Registry::createIdle. **/ explicit Idle(QObject *parent = nullptr); virtual ~Idle(); /** * @returns @c true if managing a org_kde_kwin_idle. **/ bool isValid() const; /** * Setup this Idle to manage the @p manager. * When using Registry::createIdle there is no need to call this * method. **/ void setup(org_kde_kwin_idle *manager); /** * Releases the org_kde_kwin_idle interface. * After the interface has been released the Idle instance is no * longer valid and can be setup with another org_kde_kwin_idle interface. **/ void release(); /** * Destroys the data held by this Idle. * 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_idle interface * once there is a new connection available. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, manager, &Idle::destroy); - * @endcode + * This method is automatically invoked when the Registry which created this + * Idle gets destroyed. * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating a IdleTimeout. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a IdleTimeout. **/ EventQueue *eventQueue(); /** * Creates a new IdleTimeout for the @p seat. If the @p seat has been idle, * that is none of the connected input devices got used for @p msec, the * IdleTimeout will emit the {@link IdleTimeout::idle} signal. * * It is not guaranteed that the signal will be emitted exactly at the given * timeout. A Wayland server might for example have a minimum timeout which is * larger than @p msec. * * @param msec The duration in milli seconds after which an idle timeout should fire * @param seat The Seat on which the user acitivity should be monitored. **/ IdleTimeout *getTimeout(quint32 msecs, Seat *seat, QObject *parent = nullptr); operator org_kde_kwin_idle*(); operator org_kde_kwin_idle*() 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::createIdle * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the org_kde_kwin_idle_timeout interface. * * This class is a convenient wrapper for the org_kde_kwin_idle_timeout interface. * To create a IdleTimeout call IdleTimeoutManager::getIdleTimeout. * * @see IdleTimeoutManager **/ class KWAYLANDCLIENT_EXPORT IdleTimeout : public QObject { Q_OBJECT public: /** * To create an IdleTimeout prefer using {@link Idle::getTimeout} which sets up the * IdleTimeout to be fully functional. **/ explicit IdleTimeout(QObject *parent = nullptr); virtual ~IdleTimeout(); /** * Setup this IdleTimeout to manage the @p timeout. * When using IdleTimeoutManager::createIdleTimeout there is no need to call this * method. **/ void setup(org_kde_kwin_idle_timeout *timeout); /** * Releases the org_kde_kwin_idle_timeout interface. * After the interface has been released the IdleTimeout instance is no * longer valid and can be setup with another org_kde_kwin_idle_timeout interface. **/ void release(); /** * Destroys the data held by this IdleTimeout. * 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_idle_timeout interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, source, &IdleTimeout::destroy); * @endcode * * @see release **/ void destroy(); /** * @returns @c true if managing a org_kde_kwin_idle_timeout. **/ bool isValid() const; operator org_kde_kwin_idle_timeout*(); operator org_kde_kwin_idle_timeout*() const; /** * Simulates user activity. If the IdleTimeout is in idle state this will trigger the * {@link resumeFromIdle} signal. The current idle duration is reset, so the {@link idle} * will only be emitted after a complete idle duration as requested for this IdleTimeout. */ void simulateUserActivity(); Q_SIGNALS: /** * Emitted when this IdleTimeout triggered. This means the system has been idle for * the duration specified when creating the IdleTimeout. * @see Idle::getTimeout. * @see resumeFromIdle **/ void idle(); /** * Emitted when the system shows activity again after the idle state was reached. * @see idle **/ void resumeFromIdle(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/keyboard.cpp b/src/client/keyboard.cpp index e22bcd9..29ead93 100644 --- a/src/client/keyboard.cpp +++ b/src/client/keyboard.cpp @@ -1,218 +1,219 @@ /******************************************************************** 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 "keyboard.h" #include "surface.h" #include "wayland_pointer_p.h" +#include // wayland #include namespace KWayland { namespace Client { class Keyboard::Private { public: Private(Keyboard *q); void setup(wl_keyboard *k); WaylandPointer keyboard; - Surface *enteredSurface = nullptr; + QPointer enteredSurface; struct { qint32 charactersPerSecond = 0; qint32 delay = 0; } repeatInfo; private: void enter(uint32_t serial, wl_surface *surface, wl_array *keys); void leave(uint32_t serial); static void keymapCallback(void *data, wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size); static void enterCallback(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface, wl_array *keys); static void leaveCallback(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface); static void keyCallback(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state); static void modifiersCallback(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group); static void repeatInfoCallback(void *data, wl_keyboard *keyboard, int32_t charactersPerSecond, int32_t delay); Keyboard *q; static const wl_keyboard_listener s_listener; }; Keyboard::Private::Private(Keyboard *q) : q(q) { } void Keyboard::Private::setup(wl_keyboard *k) { Q_ASSERT(k); Q_ASSERT(!keyboard); keyboard.setup(k); wl_keyboard_add_listener(keyboard, &s_listener, this); } const wl_keyboard_listener Keyboard::Private::s_listener = { keymapCallback, enterCallback, leaveCallback, keyCallback, modifiersCallback, repeatInfoCallback }; Keyboard::Keyboard(QObject *parent) : QObject(parent) , d(new Private(this)) { } Keyboard::~Keyboard() { release(); } void Keyboard::release() { d->keyboard.release(); } void Keyboard::destroy() { d->keyboard.destroy(); } void Keyboard::setup(wl_keyboard *keyboard) { d->setup(keyboard); } void Keyboard::Private::enterCallback(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface, wl_array *keys) { auto k = reinterpret_cast(data); Q_ASSERT(k->keyboard == keyboard); k->enter(serial, surface, keys); } void Keyboard::Private::enter(uint32_t serial, wl_surface *surface, wl_array *keys) { Q_UNUSED(keys) enteredSurface = Surface::get(surface); emit q->entered(serial); } void Keyboard::Private::leaveCallback(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface) { + Q_UNUSED(surface) auto k = reinterpret_cast(data); Q_ASSERT(k->keyboard == keyboard); - Q_ASSERT(*(k->enteredSurface) == surface); k->leave(serial); } void Keyboard::Private::leave(uint32_t serial) { - enteredSurface = nullptr; + enteredSurface.clear(); emit q->left(serial); } void Keyboard::Private::keyCallback(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { Q_UNUSED(serial) auto k = reinterpret_cast(data); Q_ASSERT(k->keyboard == keyboard); auto toState = [state] { if (state == WL_KEYBOARD_KEY_STATE_RELEASED) { return KeyState::Released; } else { return KeyState::Pressed; } }; emit k->q->keyChanged(key, toState(), time); } void Keyboard::Private::keymapCallback(void *data, wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size) { auto k = reinterpret_cast(data); Q_ASSERT(k->keyboard == keyboard); if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { return; } emit k->q->keymapChanged(fd, size); } void Keyboard::Private::modifiersCallback(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group) { Q_UNUSED(serial) auto k = reinterpret_cast(data); Q_ASSERT(k->keyboard == keyboard); emit k->q->modifiersChanged(modsDepressed, modsLatched, modsLocked, group); } void Keyboard::Private::repeatInfoCallback(void *data, wl_keyboard *keyboard, int32_t charactersPerSecond, int32_t delay) { auto k = reinterpret_cast(data); Q_ASSERT(k->keyboard == keyboard); k->repeatInfo.charactersPerSecond = qMax(charactersPerSecond, 0); k->repeatInfo.delay = qMax(delay, 0); emit k->q->keyRepeatChanged(); } Surface *Keyboard::enteredSurface() { - return d->enteredSurface; + return d->enteredSurface.data(); } Surface *Keyboard::enteredSurface() const { - return d->enteredSurface; + return d->enteredSurface.data(); } bool Keyboard::isValid() const { return d->keyboard.isValid(); } bool Keyboard::isKeyRepeatEnabled() const { return d->repeatInfo.charactersPerSecond > 0; } qint32 Keyboard::keyRepeatDelay() const { return d->repeatInfo.delay; } qint32 Keyboard::keyRepeatRate() const { return d->repeatInfo.charactersPerSecond; } Keyboard::operator wl_keyboard*() { return d->keyboard; } Keyboard::operator wl_keyboard*() const { return d->keyboard; } } } diff --git a/src/client/output.cpp b/src/client/output.cpp index f9ae6b6..f254763 100644 --- a/src/client/output.cpp +++ b/src/client/output.cpp @@ -1,395 +1,400 @@ /******************************************************************** Copyright 2013 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 "output.h" #include "wayland_pointer_p.h" // Qt #include #include #include #include // wayland #include namespace KWayland { namespace Client { namespace { typedef QList Modes; } class Output::Private { public: Private(Output *q); ~Private(); void setup(wl_output *o); WaylandPointer output; EventQueue *queue = nullptr; QSize physicalSize; QPoint globalPosition; QString manufacturer; QString model; int scale = 1; SubPixel subPixel = SubPixel::Unknown; Transform transform = Transform::Normal; Modes modes; Modes::iterator currentMode = modes.end(); static Output *get(wl_output *o); private: static void geometryCallback(void *data, wl_output *output, int32_t x, int32_t y, int32_t physicalWidth, int32_t physicalHeight, int32_t subPixel, const char *make, const char *model, int32_t transform); static void modeCallback(void *data, wl_output *output, uint32_t flags, int32_t width, int32_t height, int32_t refresh); static void doneCallback(void *data, wl_output *output); static void scaleCallback(void *data, wl_output *output, int32_t scale); void setPhysicalSize(const QSize &size); void setGlobalPosition(const QPoint &pos); void setManufacturer(const QString &manufacturer); void setModel(const QString &model); void setScale(int scale); void setSubPixel(SubPixel subPixel); void setTransform(Transform transform); void addMode(uint32_t flags, int32_t width, int32_t height, int32_t refresh); Output *q; static struct wl_output_listener s_outputListener; static QVector s_allOutputs; }; QVector Output::Private::s_allOutputs; Output::Private::Private(Output *q) : q(q) { s_allOutputs << this; } Output::Private::~Private() { s_allOutputs.removeOne(this); } Output *Output::Private::get(wl_output *o) { auto it = std::find_if(s_allOutputs.constBegin(), s_allOutputs.constEnd(), [o] (Private *p) { const wl_output *reference = p->output; return reference == o; } ); if (it != s_allOutputs.constEnd()) { return (*it)->q; } return nullptr; } void Output::Private::setup(wl_output *o) { Q_ASSERT(o); Q_ASSERT(!output); output.setup(o); wl_output_add_listener(output, &s_outputListener, this); } bool Output::Mode::operator==(const Output::Mode &m) const { return size == m.size && refreshRate == m.refreshRate && flags == m.flags && output == m.output; } Output::Output(QObject *parent) : QObject(parent) , d(new Private(this)) { } Output::~Output() { d->output.release(); } wl_output_listener Output::Private::s_outputListener = { geometryCallback, modeCallback, doneCallback, scaleCallback }; void Output::Private::geometryCallback(void *data, wl_output *output, int32_t x, int32_t y, int32_t physicalWidth, int32_t physicalHeight, int32_t subPixel, const char *make, const char *model, int32_t transform) { Q_UNUSED(transform) auto o = reinterpret_cast(data); Q_ASSERT(o->output == output); o->setGlobalPosition(QPoint(x, y)); o->setManufacturer(make); o->setModel(model); o->setPhysicalSize(QSize(physicalWidth, physicalHeight)); auto toSubPixel = [subPixel]() { switch (subPixel) { case WL_OUTPUT_SUBPIXEL_NONE: return SubPixel::None; case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB: return SubPixel::HorizontalRGB; case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR: return SubPixel::HorizontalBGR; case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB: return SubPixel::VerticalRGB; case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR: return SubPixel::VerticalBGR; case WL_OUTPUT_SUBPIXEL_UNKNOWN: default: return SubPixel::Unknown; } }; o->setSubPixel(toSubPixel()); auto toTransform = [transform]() { switch (transform) { case WL_OUTPUT_TRANSFORM_90: return Transform::Rotated90; case WL_OUTPUT_TRANSFORM_180: return Transform::Rotated180; case WL_OUTPUT_TRANSFORM_270: return Transform::Rotated270; case WL_OUTPUT_TRANSFORM_FLIPPED: return Transform::Flipped; case WL_OUTPUT_TRANSFORM_FLIPPED_90: return Transform::Flipped90; case WL_OUTPUT_TRANSFORM_FLIPPED_180: return Transform::Flipped180; case WL_OUTPUT_TRANSFORM_FLIPPED_270: return Transform::Flipped270; case WL_OUTPUT_TRANSFORM_NORMAL: default: return Transform::Normal; } }; o->setTransform(toTransform()); } void Output::Private::modeCallback(void *data, wl_output *output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { auto o = reinterpret_cast(data); Q_ASSERT(o->output == output); o->addMode(flags, width, height, refresh); } void Output::Private::addMode(uint32_t flags, int32_t width, int32_t height, int32_t refresh) { Mode mode; mode.output = QPointer(q); mode.refreshRate = refresh; mode.size = QSize(width, height); if (flags & WL_OUTPUT_MODE_CURRENT) { mode.flags |= Mode::Flag::Current; } if (flags & WL_OUTPUT_MODE_PREFERRED) { mode.flags |= Mode::Flag::Preferred; } auto currentIt = modes.insert(modes.end(), mode); bool existing = false; if (flags & WL_OUTPUT_MODE_CURRENT) { auto it = modes.begin(); while (it != currentIt) { auto &m = (*it); if (m.flags.testFlag(Mode::Flag::Current)) { m.flags &= ~Mode::Flags(Mode::Flag::Current); emit q->modeChanged(m); } if (m.refreshRate == mode.refreshRate && m.size == mode.size) { it = modes.erase(it); existing = true; } else { it++; } } currentMode = currentIt; } if (existing) { emit q->modeChanged(mode); } else { emit q->modeAdded(mode); } } void Output::Private::scaleCallback(void *data, wl_output *output, int32_t scale) { auto o = reinterpret_cast(data); Q_ASSERT(o->output == output); o->setScale(scale); } void Output::Private::doneCallback(void *data, wl_output *output) { auto o = reinterpret_cast(data); Q_ASSERT(o->output == output); emit o->q->changed(); } void Output::setup(wl_output *output) { d->setup(output); } EventQueue *Output::eventQueue() const { return d->queue; } void Output::setEventQueue(EventQueue *queue) { d->queue = queue; } void Output::Private::setGlobalPosition(const QPoint &pos) { globalPosition = pos; } void Output::Private::setManufacturer(const QString &m) { manufacturer = m; } void Output::Private::setModel(const QString &m) { model = m; } void Output::Private::setPhysicalSize(const QSize &size) { physicalSize = size; } void Output::Private::setScale(int s) { scale = s; } QRect Output::geometry() const { if (d->currentMode == d->modes.end()) { return QRect(); } return QRect(d->globalPosition, pixelSize()); } void Output::Private::setSubPixel(Output::SubPixel s) { subPixel = s; } void Output::Private::setTransform(Output::Transform t) { transform = t; } QPoint Output::globalPosition() const { return d->globalPosition; } QString Output::manufacturer() const { return d->manufacturer; } QString Output::model() const { return d->model; } wl_output *Output::output() { return d->output; } QSize Output::physicalSize() const { return d->physicalSize; } QSize Output::pixelSize() const { if (d->currentMode == d->modes.end()) { return QSize(); } return (*d->currentMode).size; } int Output::refreshRate() const { if (d->currentMode == d->modes.end()) { return 0; } return (*d->currentMode).refreshRate; } int Output::scale() const { return d->scale; } bool Output::isValid() const { return d->output.isValid(); } Output::SubPixel Output::subPixel() const { return d->subPixel; } Output::Transform Output::transform() const { return d->transform; } QList< Output::Mode > Output::modes() const { return d->modes; } Output::operator wl_output*() { return d->output; } Output::operator wl_output*() const { return d->output; } Output *Output::get(wl_output *o) { return Private::get(o); } +void Output::destroy() +{ + d->output.destroy(); +} + } } diff --git a/src/client/output.h b/src/client/output.h index ae92eb9..6a76341 100644 --- a/src/client/output.h +++ b/src/client/output.h @@ -1,251 +1,264 @@ /******************************************************************** Copyright 2013 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_OUTPUT_H #define WAYLAND_OUTPUT_H #include #include #include #include struct wl_output; class QPoint; class QRect; namespace KWayland { namespace Client { class EventQueue; /** * @short Wrapper for the wl_output interface. * * This class provides a convenient wrapper for the wl_output interface. * Its main purpose is to hold the information about one Output. * * To use this class one needs to interact with the Registry. There are two * possible ways to create an Output interface: * @code * Output *c = registry->createOutput(name, version); * @endcode * * This creates the Output and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * Output *c = new Output; * c->setup(registry->bindOutput(name, version)); * @endcode * * The Output can be used as a drop-in replacement for any wl_output * pointer as it provides matching cast operators. * * Please note that all properties of Output are not valid until the * changed signal has been emitted. The wayland server is pushing the * information in an async way to the Output instance. By emitting changed * the Output indicates that all relevant information is available. * * @see Registry **/ class KWAYLANDCLIENT_EXPORT Output : public QObject { Q_OBJECT public: enum class SubPixel { Unknown, None, HorizontalRGB, HorizontalBGR, VerticalRGB, VerticalBGR }; enum class Transform { Normal, Rotated90, Rotated180, Rotated270, Flipped, Flipped90, Flipped180, Flipped270 }; struct Mode { enum class Flag { None = 0, Current = 1 << 0, Preferred = 1 << 1 }; Q_DECLARE_FLAGS(Flags, Flag) /** * The size of this Mode in pixel space. **/ QSize size; /** * The refresh rate in mHz of this Mode. **/ int refreshRate = 0; /** * The flags of this Mode, that is whether it's the * Current and/or Preferred Mode of the Output. **/ Flags flags = Flag::None; /** * The Output to which this Mode belongs. **/ QPointer output; bool operator==(const Mode &m) const; }; explicit Output(QObject *parent = nullptr); virtual ~Output(); /** * Setup this Compositor to manage the @p output. * When using Registry::createOutput there is no need to call this * method. **/ void setup(wl_output *output); /** * @returns @c true if managing a wl_output. **/ bool isValid() const; operator wl_output*(); operator wl_output*() const; wl_output *output(); /** * Size in millimeters. **/ QSize physicalSize() const; /** * Position within the global compositor space. **/ QPoint globalPosition() const; /** * Textual description of the manufacturer. **/ QString manufacturer() const; /** * Textual description of the model. **/ QString model() const; /** * Size in the current mode. **/ QSize pixelSize() const; /** * The geometry of this Output in pixels. * Convenient for QRect(globalPosition(), pixelSize()). * @see globalPosition * @see pixelSize **/ QRect geometry() const; /** * Refresh rate in mHz of the current mode. **/ int refreshRate() const; /** * Scaling factor of this output. * * A scale larger than 1 means that the compositor will automatically scale surface buffers * by this amount when rendering. This is used for very high resolution displays where * applications rendering at the native resolution would be too small to be legible. **/ int scale() const; /** * Subpixel orientation of this Output. **/ SubPixel subPixel() const; /** * Transform that maps framebuffer to Output. * * The purpose is mainly to allow clients render accordingly and tell the compositor, * so that for fullscreen surfaces, the compositor will still be able to scan out * directly from client surfaces. **/ Transform transform() const; /** * @returns The Modes of this Output. **/ QList modes() const; /** * Sets the @p queue to use for bound proxies. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for bound proxies. **/ EventQueue *eventQueue() const; /** * @returns The Output for the @p native wl_output. @c null if there is no Output for it. * @since 5.27 **/ static Output *get(wl_output *native); + /** + * Destroys the data hold by this Output. + * This method is supposed to be used when the connection to the Wayland + * server goes away. If the connection is not valid any more, it's not + * possible to call release any more as that calls into the Wayland + * connection and the call would fail. + * + * This method is automatically invoked when the Registry which created this + * Output gets destroyed. + * + **/ + void destroy(); + Q_SIGNALS: /** * Emitted whenever at least one of the data changed. **/ void changed(); /** * Emitted whenever a new Mode is added. * This normally only happens during the initial promoting of modes. * Afterwards only modeChanged should be emitted. * @param mode The newly added Mode. * @see modeChanged **/ void modeAdded(const KWayland::Client::Output::Mode &mode); /** * Emitted whenever a Mode changes. * This normally means that the @c Mode::Flag::Current is added or removed. * @param mode The changed Mode **/ void modeChanged(const KWayland::Client::Output::Mode &mode); /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the Compositor got created by * Registry::createOutput * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::Output*) Q_DECLARE_METATYPE(KWayland::Client::Output::SubPixel) Q_DECLARE_METATYPE(KWayland::Client::Output::Transform) Q_DECLARE_METATYPE(KWayland::Client::Output::Mode) Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Client::Output::Mode::Flags) #endif diff --git a/src/client/outputconfiguration.h b/src/client/outputconfiguration.h index 6270443..7e7faa8 100644 --- a/src/client/outputconfiguration.h +++ b/src/client/outputconfiguration.h @@ -1,246 +1,245 @@ /**************************************************************************** * Copyright 2015 Sebastian Kügler * * 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_OUTPUTCONFIGURATION_H #define KWAYLAND_CLIENT_OUTPUTCONFIGURATION_H #include #include #include "outputdevice.h" #include struct org_kde_kwin_outputmanagement; struct org_kde_kwin_outputconfiguration; namespace KWayland { namespace Client { class EventQueue; /** @class OutputConfiguration * * OutputConfiguration provides access to changing OutputDevices. The interface is async * and atomic. An OutputConfiguration is created through OutputManagement::createConfiguration(). * * The overall mechanism is to get a new OutputConfiguration from the OutputManagement global and * apply changes through the OutputConfiguration::set* calls. When all changes are set, the client * calls apply, which asks the server to look at the changes and apply them. The server will then * signal back whether the changes have been applied successfully (@c applied()) or were rejected * or failed to apply (@c failed()). * * The current settings for outputdevices can be gotten from @c Registry::outputDevices(), these * are used in the set* calls to identify the output the setting applies to. * * These KWayland classes will not apply changes to the OutputDevices, this is the compositor's * task. As such, the configuration set through this interface can be seen as a hint what the * compositor should set up, but whether or not the compositor does it (based on hardware or * rendering policies, for example), is up to the compositor. The mode setting is passed on to * the DRM subsystem through the compositor. The compositor also saves this configuration and reads * it on startup, this interface is not involved in that process. * * @c apply() should only be called after changes to all output devices have been made, not after * each change. This allows to test the new configuration as a whole, and is a lot faster since * hardware changes can be tested in their new combination, they done in parallel.and rolled back * as a whole. * * \verbatim // We're just picking the first of our outputdevices KWayland::Client::OutputDevice *output = m_clientOutputs.first(); // Create a new configuration object auto config = m_outputManagement.createConfiguration(); // handle applied and failed signals connect(config, &OutputConfiguration::applied, []() { qDebug() << "Configuration applied!"; }); connect(config, &OutputConfiguration::failed, []() { qDebug() << "Configuration failed!"; }); // Change settings config->setMode(output, m_clientOutputs.first()->modes().last().id); config->setTransform(output, OutputDevice::Transform::Normal); config->setPosition(output, QPoint(0, 1920)); config->setScale(output, 2); // Now ask the compositor to apply the changes config->apply(); // You may wait for the applied() or failed() signal here \endverbatim * @see OutputDevice * @see OutputManagement * @see OutputManagement::createConfiguration() * @since 5.5 */ class KWAYLANDCLIENT_EXPORT OutputConfiguration : public QObject { Q_OBJECT public: virtual ~OutputConfiguration(); /** * Setup this OutputConfiguration to manage the @p outputconfiguration. * When using OutputManagement::createOutputConfiguration there is no need to call this * method. * @param outputconfiguration the outputconfiguration object to set up. **/ void setup(org_kde_kwin_outputconfiguration *outputconfiguration); /** * @returns @c true if managing a org_kde_kwin_outputconfiguration. **/ bool isValid() const; /** * Releases the org_kde_kwin_outputconfiguration interface. * After the interface has been released the OutputConfiguration instance is no * longer valid and can be setup with another org_kde_kwin_outputconfiguration interface. **/ void release(); /** * Destroys the data held by this OutputConfiguration. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid any more, it's not * possible to call release any more 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 setup to a new org_kde_kwin_outputconfiguration interface * once there is a new connection available. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, outputconfiguration, &OutputConfiguration::destroy); - * @endcode + * This method is automatically invoked when the Registry which created this + * OutputConfiguration gets destroyed. + * * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating a OutputConfiguration. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a OutputConfiguration **/ EventQueue *eventQueue(); /** * Enable or disable an output. Enabled means it's used by the * compositor for rendering, Disabled means, that no wl_output * is connected to this, and the device is sitting there unused * by this compositor. * The changes done in this call will be recorded in the * OutputDevice and only applied after apply() has been called. * * @param outputdevice the OutputDevice this change applies to. * @param enable new Enablement state of this output device. */ void setEnabled(OutputDevice *outputdevice, OutputDevice::Enablement enable); /** * Set the mode of this output, identified by its mode id. * The changes done in this call will be recorded in the * OutputDevice and only applied after apply() has been called. * * @param outputdevice the OutputDevice this change applies to. * @param modeId the id of the mode. */ void setMode(OutputDevice *outputdevice, const int modeId); /** * Set transformation for this output, for example rotated or flipped. * The changes done in this call will be recorded in the * OutputDevice and only applied after apply() has been called. * * @param outputdevice the OutputDevice this change applies to. * @param scale the scaling factor for this output device. */ void setTransform(OutputDevice *outputdevice, KWayland::Client::OutputDevice::Transform transform); /** * Position this output in the global space, relative to other outputs. * QPoint(0, 0) for top-left. The position is the top-left corner of this output. * There may not be gaps between outputs, they have to be positioned adjacend to * each other. * The changes done in this call will be recorded in the * OutputDevice and only applied after apply() has been called. * * @param outputdevice the OutputDevice this change applies to. * @param pos the OutputDevice global position relative to other outputs, * */ void setPosition(OutputDevice *outputdevice, const QPoint &pos); /** * Scale rendering of this output. * The changes done in this call will be recorded in the * OutputDevice and only applied after apply() has been called. * * @param scale the scaling factor for this output device. * @param outputdevice the OutputDevice this change applies to. */ void setScale(OutputDevice *outputdevice, qint32 scale); /** * Ask the compositor to apply the changes. * This results in the compositor looking at all outputdevices and if they have * pending changes from the set* calls, these changes will be tested with the * hardware and applied if possible. The compositor will react to these changes * with the applied() or failed() signals. Note that mode setting may take a * while, so the interval between calling apply() and receiving the applied() * signal may be considerable, depending on the hardware. * * @see applied() * @see failed() */ void apply(); operator org_kde_kwin_outputconfiguration*(); operator org_kde_kwin_outputconfiguration*() const; Q_SIGNALS: /** * The server has applied all settings successfully. Pending changes in the * OutputDevices have been cleared, changed signals from the OutputDevice have * been emitted. */ void applied(); /** * The server has failed to apply the settings or rejected them. Pending changes * in the * OutputDevices have been cleared. No changes have been applied to the * OutputDevices. */ void failed(); private: friend class OutputManagement; explicit OutputConfiguration(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::OutputConfiguration*) #endif diff --git a/src/client/outputdevice.cpp b/src/client/outputdevice.cpp index 955dcb4..1dd07d3 100644 --- a/src/client/outputdevice.cpp +++ b/src/client/outputdevice.cpp @@ -1,444 +1,449 @@ /******************************************************************** Copyright 2013 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 "logging_p.h" #include "outputdevice.h" #include "wayland_pointer_p.h" // Qt #include #include #include #include // wayland #include "wayland-org_kde_kwin_outputdevice-client-protocol.h" #include namespace KWayland { namespace Client { typedef QList Modes; class OutputDevice::Private { public: Private(OutputDevice *q); void setup(org_kde_kwin_outputdevice *o); WaylandPointer output; EventQueue *queue = nullptr; QSize physicalSize; QPoint globalPosition; QString manufacturer; QString model; int scale = 1; SubPixel subPixel = SubPixel::Unknown; Transform transform = Transform::Normal; Modes modes; Modes::iterator currentMode = modes.end(); QByteArray edid; OutputDevice::Enablement enabled = OutputDevice::Enablement::Enabled; QByteArray uuid; bool done = false; private: static void geometryCallback(void *data, org_kde_kwin_outputdevice *output, int32_t x, int32_t y, int32_t physicalWidth, int32_t physicalHeight, int32_t subPixel, const char *make, const char *model, int32_t transform); static void modeCallback(void *data, org_kde_kwin_outputdevice *output, uint32_t flags, int32_t width, int32_t height, int32_t refresh, int32_t mode_id); static void doneCallback(void *data, org_kde_kwin_outputdevice *output); static void scaleCallback(void *data, org_kde_kwin_outputdevice *output, int32_t scale); static void edidCallback(void *data, org_kde_kwin_outputdevice *output, const char *raw); static void enabledCallback(void *data, org_kde_kwin_outputdevice *output, int32_t enabled); static void uuidCallback(void *data, org_kde_kwin_outputdevice *output, const char *uuid); void setPhysicalSize(const QSize &size); void setGlobalPosition(const QPoint &pos); void setManufacturer(const QString &manufacturer); void setModel(const QString &model); void setScale(int scale); void setSubPixel(SubPixel subPixel); void setTransform(Transform transform); void addMode(uint32_t flags, int32_t width, int32_t height, int32_t refresh, int32_t mode_id); OutputDevice *q; static struct org_kde_kwin_outputdevice_listener s_outputListener; }; OutputDevice::Private::Private(OutputDevice *q) : q(q) { } void OutputDevice::Private::setup(org_kde_kwin_outputdevice *o) { Q_ASSERT(o); Q_ASSERT(!output); output.setup(o); org_kde_kwin_outputdevice_add_listener(output, &s_outputListener, this); } bool OutputDevice::Mode::operator==(const OutputDevice::Mode &m) const { return size == m.size && refreshRate == m.refreshRate && flags == m.flags && output == m.output; } OutputDevice::OutputDevice(QObject *parent) : QObject(parent) , d(new Private(this)) { } OutputDevice::~OutputDevice() { d->output.release(); } org_kde_kwin_outputdevice_listener OutputDevice::Private::s_outputListener = { geometryCallback, modeCallback, doneCallback, scaleCallback, edidCallback, enabledCallback, uuidCallback }; void OutputDevice::Private::geometryCallback(void *data, org_kde_kwin_outputdevice *output, int32_t x, int32_t y, int32_t physicalWidth, int32_t physicalHeight, int32_t subPixel, const char *make, const char *model, int32_t transform) { Q_UNUSED(transform) auto o = reinterpret_cast(data); Q_ASSERT(o->output == output); o->setGlobalPosition(QPoint(x, y)); o->setManufacturer(make); o->setModel(model); o->setPhysicalSize(QSize(physicalWidth, physicalHeight)); auto toSubPixel = [subPixel]() { switch (subPixel) { case WL_OUTPUT_SUBPIXEL_NONE: return SubPixel::None; case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB: return SubPixel::HorizontalRGB; case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR: return SubPixel::HorizontalBGR; case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB: return SubPixel::VerticalRGB; case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR: return SubPixel::VerticalBGR; case WL_OUTPUT_SUBPIXEL_UNKNOWN: default: return SubPixel::Unknown; } }; o->setSubPixel(toSubPixel()); auto toTransform = [transform]() { switch (transform) { case WL_OUTPUT_TRANSFORM_90: return Transform::Rotated90; case WL_OUTPUT_TRANSFORM_180: return Transform::Rotated180; case WL_OUTPUT_TRANSFORM_270: return Transform::Rotated270; case WL_OUTPUT_TRANSFORM_FLIPPED: return Transform::Flipped; case WL_OUTPUT_TRANSFORM_FLIPPED_90: return Transform::Flipped90; case WL_OUTPUT_TRANSFORM_FLIPPED_180: return Transform::Flipped180; case WL_OUTPUT_TRANSFORM_FLIPPED_270: return Transform::Flipped270; case WL_OUTPUT_TRANSFORM_NORMAL: default: return Transform::Normal; } }; o->setTransform(toTransform()); } void OutputDevice::Private::modeCallback(void *data, org_kde_kwin_outputdevice *output, uint32_t flags, int32_t width, int32_t height, int32_t refresh, int32_t mode_id) { auto o = reinterpret_cast(data); Q_ASSERT(o->output == output); o->addMode(flags, width, height, refresh, mode_id); } void OutputDevice::Private::addMode(uint32_t flags, int32_t width, int32_t height, int32_t refresh, int32_t mode_id) { Mode mode; mode.output = QPointer(q); mode.refreshRate = refresh; mode.size = QSize(width, height); mode.id = mode_id; if (flags & WL_OUTPUT_MODE_CURRENT) { mode.flags |= Mode::Flag::Current; } if (flags & WL_OUTPUT_MODE_PREFERRED) { mode.flags |= Mode::Flag::Preferred; } auto currentIt = modes.insert(modes.end(), mode); bool existing = false; if (flags & WL_OUTPUT_MODE_CURRENT) { auto it = modes.begin(); while (it != currentIt) { auto &m = (*it); if (m.flags.testFlag(Mode::Flag::Current)) { m.flags &= ~Mode::Flags(Mode::Flag::Current); emit q->modeChanged(m); } if (m.refreshRate == mode.refreshRate && m.size == mode.size) { it = modes.erase(it); existing = true; } else { it++; } } currentMode = currentIt; } if (existing) { emit q->modeChanged(mode); } else { emit q->modeAdded(mode); } } KWayland::Client::OutputDevice::Mode OutputDevice::currentMode() const { for (const auto &m: modes()) { if (m.flags.testFlag(KWayland::Client::OutputDevice::Mode::Flag::Current)) { return m; } } qCWarning(KWAYLAND_CLIENT) << "current mode not found"; return Mode(); } void OutputDevice::Private::scaleCallback(void *data, org_kde_kwin_outputdevice *output, int32_t scale) { auto o = reinterpret_cast(data); Q_ASSERT(o->output == output); o->setScale(scale); } void OutputDevice::Private::doneCallback(void *data, org_kde_kwin_outputdevice *output) { auto o = reinterpret_cast(data); Q_ASSERT(o->output == output); o->done = true; emit o->q->changed(); emit o->q->done(); } void OutputDevice::Private::edidCallback(void* data, org_kde_kwin_outputdevice* output, const char* raw) { Q_UNUSED(output); auto o = reinterpret_cast(data); o->edid = QByteArray::fromBase64(raw); } void OutputDevice::Private::enabledCallback(void* data, org_kde_kwin_outputdevice* output, int32_t enabled) { Q_UNUSED(output); auto o = reinterpret_cast(data); OutputDevice::Enablement _enabled = OutputDevice::Enablement::Disabled; if (enabled == ORG_KDE_KWIN_OUTPUTDEVICE_ENABLEMENT_ENABLED) { _enabled = OutputDevice::Enablement::Enabled; } if (o->enabled != _enabled) { o->enabled = _enabled; emit o->q->enabledChanged(o->enabled); if (o->done) { emit o->q->changed(); } } } void OutputDevice::Private::uuidCallback(void* data, org_kde_kwin_outputdevice* output, const char *uuid) { Q_UNUSED(output); auto o = reinterpret_cast(data); if (o->uuid != uuid) { o->uuid = uuid; emit o->q->uuidChanged(o->uuid); if (o->done) { emit o->q->changed(); } } } void OutputDevice::setup(org_kde_kwin_outputdevice *output) { d->setup(output); } EventQueue *OutputDevice::eventQueue() const { return d->queue; } void OutputDevice::setEventQueue(EventQueue *queue) { d->queue = queue; } void OutputDevice::Private::setGlobalPosition(const QPoint &pos) { globalPosition = pos; } void OutputDevice::Private::setManufacturer(const QString &m) { manufacturer = m; } void OutputDevice::Private::setModel(const QString &m) { model = m; } void OutputDevice::Private::setPhysicalSize(const QSize &size) { physicalSize = size; } void OutputDevice::Private::setScale(int s) { scale = s; } QRect OutputDevice::geometry() const { if (d->currentMode == d->modes.end()) { return QRect(); } return QRect(d->globalPosition, pixelSize()); } void OutputDevice::Private::setSubPixel(OutputDevice::SubPixel s) { subPixel = s; } void OutputDevice::Private::setTransform(OutputDevice::Transform t) { transform = t; } QPoint OutputDevice::globalPosition() const { return d->globalPosition; } QString OutputDevice::manufacturer() const { return d->manufacturer; } QString OutputDevice::model() const { return d->model; } org_kde_kwin_outputdevice *OutputDevice::output() { return d->output; } QSize OutputDevice::physicalSize() const { return d->physicalSize; } QSize OutputDevice::pixelSize() const { if (d->currentMode == d->modes.end()) { return QSize(); } return (*d->currentMode).size; } int OutputDevice::refreshRate() const { if (d->currentMode == d->modes.end()) { return 0; } return (*d->currentMode).refreshRate; } int OutputDevice::scale() const { return d->scale; } bool OutputDevice::isValid() const { return d->output.isValid(); } OutputDevice::SubPixel OutputDevice::subPixel() const { return d->subPixel; } OutputDevice::Transform OutputDevice::transform() const { return d->transform; } QList< OutputDevice::Mode > OutputDevice::modes() const { return d->modes; } OutputDevice::operator org_kde_kwin_outputdevice*() { return d->output; } OutputDevice::operator org_kde_kwin_outputdevice*() const { return d->output; } QByteArray OutputDevice::edid() const { return d->edid; } OutputDevice::Enablement OutputDevice::enabled() const { return d->enabled; } QByteArray OutputDevice::uuid() const { return d->uuid; } +void OutputDevice::destroy() +{ + d->output.destroy(); + +} } } diff --git a/src/client/outputdevice.h b/src/client/outputdevice.h index ab8ca07..6cbcc4a 100644 --- a/src/client/outputdevice.h +++ b/src/client/outputdevice.h @@ -1,287 +1,297 @@ /******************************************************************** Copyright 2013 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_OUTPUTDEVICE_H #define WAYLAND_OUTPUTDEVICE_H #include #include #include #include struct org_kde_kwin_outputdevice; class QPoint; class QRect; namespace KWayland { namespace Client { class EventQueue; /** * @short Wrapper for the org_kde_kwin_outputdevice interface. * * This class provides a convenient wrapper for the org_kde_kwin_outputdevice interface. * Its main purpose is to hold the information about one OutputDevice. * * To use this class one needs to interact with the Registry. There are two * possible ways to create an OutputDevice interface: * @code * OutputDevice *c = registry->createOutputDevice(name, version); * @endcode * * This creates the OutputDevice and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * OutputDevice *c = new OutputDevice; * c->setup(registry->bindOutputDevice(name, version)); * @endcode * * The OutputDevice can be used as a drop-in replacement for any org_kde_kwin_outputdevice * pointer as it provides matching cast operators. * * Please note that all properties of OutputDevice are not valid until the * changed signal has been emitted. The wayland server is pushing the * information in an async way to the OutputDevice instance. By emitting changed * the OutputDevice indicates that all relevant information is available. * * @see Registry * @since 5.5 **/ class KWAYLANDCLIENT_EXPORT OutputDevice : public QObject { Q_OBJECT public: enum class SubPixel { Unknown, None, HorizontalRGB, HorizontalBGR, VerticalRGB, VerticalBGR }; enum class Transform { Normal, Rotated90, Rotated180, Rotated270, Flipped, Flipped90, Flipped180, Flipped270 }; enum class Enablement { Disabled = 0, Enabled = 1 }; struct Mode { enum class Flag { None = 0, Current = 1 << 0, Preferred = 1 << 1 }; Q_DECLARE_FLAGS(Flags, Flag) /** * The size of this Mode in pixel space. **/ QSize size; /** * The refresh rate in mHz of this Mode. **/ int refreshRate = 0; /** * The flags of this Mode, that is whether it's the * Current and/or Preferred Mode of the OutputDevice. **/ Flags flags = Flag::None; /** * The OutputDevice to which this Mode belongs. **/ QPointer output; /** * The id of this mode, unique per OutputDevice. This id can be used to call * OutputConfiguration->setMode(); * @see OutputConfiguration::setMode **/ int id; bool operator==(const Mode &m) const; }; explicit OutputDevice(QObject *parent = nullptr); virtual ~OutputDevice(); /** * Setup this Compositor to manage the @p output. * When using Registry::createOutputDevice there is no need to call this * method. **/ void setup(org_kde_kwin_outputdevice *output); /** * @returns @c true if managing a org_kde_kwin_outputdevice. **/ bool isValid() const; operator org_kde_kwin_outputdevice*(); operator org_kde_kwin_outputdevice*() const; org_kde_kwin_outputdevice *output(); /** * Size in millimeters. **/ QSize physicalSize() const; /** * Position within the global compositor space. **/ QPoint globalPosition() const; /** * Textual description of the manufacturer. **/ QString manufacturer() const; /** * Textual description of the model. **/ QString model() const; /** * Size in the current mode. **/ QSize pixelSize() const; /** * The geometry of this OutputDevice in pixels. * Convenient for QRect(globalPosition(), pixelSize()). * @see globalPosition * @see pixelSize **/ QRect geometry() const; /** * Refresh rate in mHz of the current mode. **/ int refreshRate() const; /** * Scaling factor of this output. * * A scale larger than 1 means that the compositor will automatically scale surface buffers * by this amount when rendering. This is used for very high resolution displays where * applications rendering at the native resolution would be too small to be legible. **/ int scale() const; /** * Subpixel orientation of this OutputDevice. **/ SubPixel subPixel() const; /** * Transform that maps framebuffer to OutputDevice. * * The purpose is mainly to allow clients render accordingly and tell the compositor, * so that for fullscreen surfaces, the compositor will still be able to scan out * directly from client surfaces. **/ Transform transform() const; /** * @returns The Modes of this OutputDevice. **/ QList modes() const; KWayland::Client::OutputDevice::Mode currentMode() const; /** * Sets the @p queue to use for bound proxies. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for bound proxies. **/ EventQueue *eventQueue() const; /** * @returns The EDID information for this output. **/ QByteArray edid() const; /** * @returns Whether this output is enabled or not. **/ OutputDevice::Enablement enabled() const; /** * @returns A unique identifier for this outputdevice, determined by the server. **/ QByteArray uuid() const; - + /** + * Destroys the data hold by this OutputDevice. + * This method is supposed to be used when the connection to the Wayland + * server goes away. If the connection is not valid any more, it's not + * possible to call release any more as that calls into the Wayland + * connection and the call would fail. + * + * This method is automatically invoked when the Registry which created this + * Output gets destroyed. + **/ + void destroy(); Q_SIGNALS: /** * Emitted when the output is fully initialized. **/ void done(); /** * Emitted whenever at least one of the data changed. **/ void changed(); /** * Emitted whenever the enabled property changes. **/ void enabledChanged(OutputDevice::Enablement enabled); /** * Emitted whenever the id property changes. **/ void uuidChanged(const QByteArray &uuid); /** * Emitted whenever a new Mode is added. * This normally only happens during the initial promoting of modes. * Afterwards only modeChanged should be emitted. * @param mode The newly added Mode. * @see modeChanged **/ void modeAdded(const KWayland::Client::OutputDevice::Mode &mode); /** * Emitted whenever a Mode changes. * This normally means that the @c Mode::Flag::Current is added or removed. * @param mode The changed Mode **/ void modeChanged(const KWayland::Client::OutputDevice::Mode &mode); /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the OutputDevice got created by * Registry::createOutputDevice * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::OutputDevice::SubPixel) Q_DECLARE_METATYPE(KWayland::Client::OutputDevice::Transform) Q_DECLARE_METATYPE(KWayland::Client::OutputDevice::Enablement) Q_DECLARE_METATYPE(KWayland::Client::OutputDevice::Mode) Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Client::OutputDevice::Mode::Flags) #endif diff --git a/src/client/outputmanagement.h b/src/client/outputmanagement.h index 67c2010..cb74638 100644 --- a/src/client/outputmanagement.h +++ b/src/client/outputmanagement.h @@ -1,142 +1,140 @@ /**************************************************************************** * Copyright 2015 Sebastian Kügler * * 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_OUTPUTMANAGEMENT_H #define KWAYLAND_CLIENT_OUTPUTMANAGEMENT_H #include #include struct org_kde_kwin_outputmanagement; struct org_kde_kwin_outputconfiguration; namespace KWayland { namespace Client { class EventQueue; class OutputDevice; class OutputConfiguration; /** * @short Wrapper for the org_kde_kwin_outputmanagement interface. * * This class provides a convenient wrapper for the org_kde_kwin_outputmanagement interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the OutputManagement interface: * @code * OutputManagement *c = registry->createOutputManagement(name, version); * @endcode * * This creates the OutputManagement and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * OutputManagement *c = new OutputManagement; * c->setup(registry->bindOutputManagement(name, version)); * @endcode * * The OutputManagement can be used as a drop-in replacement for any org_kde_kwin_outputmanagement * pointer as it provides matching cast operators. * * @see Registry * @since 5.5 **/ class KWAYLANDCLIENT_EXPORT OutputManagement : public QObject { Q_OBJECT public: /** * Creates a new OutputManagement. * Note: after constructing the OutputManagement it is not yet valid and one needs * to call setup. In order to get a ready to use OutputManagement prefer using * Registry::createOutputManagement. **/ explicit OutputManagement(QObject *parent = nullptr); virtual ~OutputManagement(); /** * Setup this OutputManagement to manage the @p outputmanagement. * When using Registry::createOutputManagement there is no need to call this * method. **/ void setup(org_kde_kwin_outputmanagement *outputmanagement); /** * @returns @c true if managing a org_kde_kwin_outputmanagement. **/ bool isValid() const; /** * Releases the org_kde_kwin_outputmanagement interface. * After the interface has been released the OutputManagement instance is no * longer valid and can be setup with another org_kde_kwin_outputmanagement interface. **/ void release(); /** * Destroys the data hold by this OutputManagement. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid any more, it's not * possible to call release any more 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 setup to a new org_kde_kwin_outputmanagement interface * once there is a new connection available. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, outputmanagement, &OutputManagement::destroy); - * @endcode + * This method is automatically invoked when the Registry which created this + * OutputManagement gets destroyed. * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this OutputManagement. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this OutputManagement. **/ EventQueue *eventQueue(); OutputConfiguration *createConfiguration(QObject *parent = nullptr); operator org_kde_kwin_outputmanagement*(); operator org_kde_kwin_outputmanagement*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the OutputManagement got created by * Registry::createOutputManagement **/ void removed(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/plasmashell.h b/src/client/plasmashell.h index 5f2c655..8a61246 100644 --- a/src/client/plasmashell.h +++ b/src/client/plasmashell.h @@ -1,362 +1,360 @@ /******************************************************************** 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 WAYLAND_PLASMASHELL_H #define WAYLAND_PLASMASHELL_H #include #include #include struct wl_surface; struct org_kde_plasma_shell; struct org_kde_plasma_surface; namespace KWayland { namespace Client { class EventQueue; class Surface; class PlasmaShellSurface; /** * @short Wrapper for the org_kde_plasma_shell interface. * * This class provides a convenient wrapper for the org_kde_plasma_shell interface. * It's main purpose is to create a PlasmaShellSurface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the Shell interface: * @code * PlasmaShell *s = registry->createPlasmaShell(name, version); * @endcode * * This creates the PlasmaShell and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * PlasmaShell *s = new PlasmaShell; * s->setup(registry->bindPlasmaShell(name, version)); * @endcode * * The PlasmaShell can be used as a drop-in replacement for any org_kde_plasma_shell * pointer as it provides matching cast operators. * * @see Registry * @see PlasmaShellSurface **/ class KWAYLANDCLIENT_EXPORT PlasmaShell : public QObject { Q_OBJECT public: explicit PlasmaShell(QObject *parent = nullptr); virtual ~PlasmaShell(); /** * @returns @c true if managing a org_kde_plasma_shell. **/ bool isValid() const; /** * Releases the org_kde_plasma_shell interface. * After the interface has been released the PlasmaShell instance is no * longer valid and can be setup with another org_kde_plasma_shell interface. * * Right before the interface is released the signal interfaceAboutToBeReleased is emitted. * @see interfaceAboutToBeReleased **/ void release(); /** * Destroys the data held by this PlasmaShell. * This method is supposed to be used when the connection to the Wayland * server goes away. Once the connection becomes invalid, 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_plasma_shell interface * once there is a new connection available. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, shell, &PlasmaShell::destroy); - * @endcode + * This method is automatically invoked when the Registry which created this + * PlasmaShell gets destroyed. * * Right before the data is destroyed, the signal interfaceAboutToBeDestroyed is emitted. * * @see release * @see interfaceAboutToBeDestroyed **/ void destroy(); /** * Setup this Shell to manage the @p shell. * When using Registry::createShell there is no need to call this * method. **/ void setup(org_kde_plasma_shell *shell); /** * Sets the @p queue to use for creating a Surface. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a Surface. **/ EventQueue *eventQueue(); /** * Creates a PlasmaShellSurface for the given @p surface and sets it up. * * If a PlasmaShellSurface for the given @p surface has already been created * a pointer to the existing one is returned instead of creating a new surface. * * @param surface The native surface to create the PlasmaShellSurface for * @param parent The parent to use for the PlasmaShellSurface * @returns created PlasmaShellSurface **/ PlasmaShellSurface *createSurface(wl_surface *surface, QObject *parent = nullptr); /** * Creates a PlasmaShellSurface for the given @p surface and sets it up. * * If a PlasmaShellSurface for the given @p surface has already been created * a pointer to the existing one is returned instead of creating a new surface. * * @param surface The Surface to create the PlasmaShellSurface for * @param parent The parent to use for the PlasmaShellSurface * @returns created PlasmaShellSurface **/ PlasmaShellSurface *createSurface(Surface *surface, QObject *parent = nullptr); operator org_kde_plasma_shell*(); operator org_kde_plasma_shell*() const; Q_SIGNALS: /** * This signal is emitted right before the interface is released. **/ void interfaceAboutToBeReleased(); /** * This signal is emitted right before the data is destroyed. **/ void interfaceAboutToBeDestroyed(); /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the Compositor got created by * Registry::createPlasmaShell * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the org_kde_plasma_surface interface. * * This class is a convenient wrapper for the org_kde_plasma_surface interface. * * To create an instance use PlasmaShell::createSurface. * * A PlasmaShellSurface is a privileged Surface which can add further hints to the * Wayland server about it's position and the usage role. The Wayland server is allowed * to ignore all requests. * * Even if a PlasmaShellSurface is created for a Surface a normal ShellSurface (or similar) * needs to be created to have the Surface mapped as a window by the Wayland server. * * @see PlasmaShell * @see Surface **/ class KWAYLANDCLIENT_EXPORT PlasmaShellSurface : public QObject { Q_OBJECT public: explicit PlasmaShellSurface(QObject *parent); virtual ~PlasmaShellSurface(); /** * Releases the org_kde_plasma_surface interface. * After the interface has been released the PlasmaShellSurface instance is no * longer valid and can be setup with another org_kde_plasma_surface interface. * * This method is automatically invoked when the PlasmaShell which created this * PlasmaShellSurface gets released. **/ void release(); /** * Destroys the data held by this PlasmaShellSurface. * 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_plasma_surface interface * once there is a new connection available. * * This method is automatically invoked when the PlasmaShell which created this * PlasmaShellSurface gets destroyed. * * @see release **/ void destroy(); /** * Setup this PlasmaShellSurface to manage the @p surface. * There is normally no need to call this method as it's invoked by * PlasmaShell::createSurface. **/ void setup(org_kde_plasma_surface *surface); /** * @returns the PlasmaShellSurface * associated with surface, * if any, nullptr if not found. * @since 5.6 */ static PlasmaShellSurface *get(Surface *surf); /** * @returns @c true if managing a org_kde_plasma_surface. **/ bool isValid() const; operator org_kde_plasma_surface*(); operator org_kde_plasma_surface*() const; /** * Describes possible roles this PlasmaShellSurface can have. * The role can be used by the Wayland server to e.g. change the stacking order accordingly. **/ enum class Role { Normal, ///< A normal Surface Desktop, ///< The Surface represents a desktop, normally stacked below all other surfaces Panel, ///< The Surface represents a panel (dock), normally stacked above normal surfaces OnScreenDisplay, ///< The Surface represents an on screen display, like a volume changed notification Notification, ///< The Surface represents a notification @since 5.24 ToolTip ///< The Surface represents a tooltip @since 5.24 }; /** * Changes the requested Role to @p role. * @see role **/ void setRole(Role role); /** * @returns The requested Role, default value is @c Role::Normal. * @see setRole **/ Role role() const; /** * Requests to position this PlasmaShellSurface at @p point in global coordinates. **/ void setPosition(const QPoint &point); /** * Describes how a PlasmaShellSurface with role @c Role::Panel should behave. * @see Role **/ enum class PanelBehavior { AlwaysVisible, AutoHide, WindowsCanCover, WindowsGoBelow }; /** * Sets the PanelBehavior for a PlasmaShellSurface with Role @c Role::Panel * @see setRole **/ void setPanelBehavior(PanelBehavior behavior); /** * Setting this bit to the window, will make it say it prefers * to not be listed in the taskbar. Taskbar implementations * may or may not follow this hint. * @since 5.5 */ void setSkipTaskbar(bool skip); /** * Requests to hide a surface with Role Panel and PanelBahvior AutoHide. * * Once the compositor has hidden the panel the signal {@link autoHidePanelHidden} gets * emitted. Once it is shown again the signal {@link autoHidePanelShown} gets emitted. * * To show the surface again from client side use {@link requestShowAutoHidingPanel}. * * @see autoHidePanelHidden * @see autoHidePanelShown * @see requestShowAutoHidingPanel * @since 5.28 **/ void requestHideAutoHidingPanel(); /** * Requests to show a surface with Role Panel and PanelBahvior AutoHide. * * This request allows the client to show a surface which it previously * requested to be hidden with {@link requestHideAutoHidingPanel}. * * @see autoHidePanelHidden * @see autoHidePanelShown * @see requestHideAutoHidingPanel * @since 5.28 **/ void requestShowAutoHidingPanel(); /** * Set whether a PlasmaShellSurface with Role Panel should get focus or not. * * By default a Panel does not take focus. With this request the compositor * can be instructed to also pass focus to a panel * * @param takesFocus Set to @c true if the Panel should gain focus. * @since 5.28 **/ void setPanelTakesFocus(bool takesFocus); Q_SIGNALS: /** * Emitted when the compositor hided an auto hiding panel. * @see requestHideAutoHidingPanel * @see autoHidePanelShown * @see requestShowAutoHidingPanel * @since 5.28 **/ void autoHidePanelHidden(); /** * Emitted when the compositor showed an auto hiding panel. * @see requestHideAutoHidingPanel * @see autoHidePanelHidden * @see requestShowAutoHidingPanel * @since 5.28 **/ void autoHidePanelShown(); private: friend class PlasmaShell; class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::PlasmaShellSurface::Role) Q_DECLARE_METATYPE(KWayland::Client::PlasmaShellSurface::PanelBehavior) #endif diff --git a/src/client/plasmawindowmanagement.h b/src/client/plasmawindowmanagement.h index fa00456..0df821a 100644 --- a/src/client/plasmawindowmanagement.h +++ b/src/client/plasmawindowmanagement.h @@ -1,627 +1,625 @@ /******************************************************************** 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 WAYLAND_PLASMAWINDOWMANAGEMENT_H #define WAYLAND_PLASMAWINDOWMANAGEMENT_H #include #include #include #include struct org_kde_plasma_window_management; struct org_kde_plasma_window; namespace KWayland { namespace Client { class EventQueue; class PlasmaWindow; class PlasmaWindowModel; class Surface; /** * @short Wrapper for the org_kde_plasma_window_management interface. * * PlasmaWindowManagement is a privileged interface. A Wayland compositor is allowed to ignore * any requests. The PlasmaWindowManagement allows to get information about the overall windowing * system. It allows to see which windows are currently available and thus is the base to implement * e.g. a task manager. * * This class provides a convenient wrapper for the org_kde_plasma_window_management interface. * It's main purpose is to create a PlasmaWindowManagementSurface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the Shell interface: * @code * PlasmaWindowManagement *s = registry->createPlasmaWindowManagement(name, version); * @endcode * * This creates the PlasmaWindowManagement and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * PlasmaWindowManagement *s = new PlasmaWindowManagement; * s->setup(registry->bindPlasmaWindowManagement(name, version)); * @endcode * * The PlasmaWindowManagement can be used as a drop-in replacement for any org_kde_plasma_window_management * pointer as it provides matching cast operators. * * @see Registry * @see PlasmaWindowManagementSurface **/ class KWAYLANDCLIENT_EXPORT PlasmaWindowManagement : public QObject { Q_OBJECT public: explicit PlasmaWindowManagement(QObject *parent = nullptr); virtual ~PlasmaWindowManagement(); /** * @returns @c true if managing a org_kde_plasma_window_management. **/ bool isValid() const; /** * Releases the org_kde_plasma_window_management interface. * After the interface has been released the PlasmaWindowManagement instance is no * longer valid and can be setup with another org_kde_plasma_window_management interface. * * Right before the interface is released the signal interfaceAboutToBeReleased is emitted. * @see interfaceAboutToBeReleased **/ void release(); /** * Destroys the data held by this PlasmaWindowManagement. * This method is supposed to be used when the connection to the Wayland * server goes away. Once the connection becomes invalid, 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_plasma_window_management interface * once there is a new connection available. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, shell, &PlasmaWindowManagement::destroy); - * @endcode + * This method is automatically invoked when the Registry which created this + * PlasmaWindowManagement gets destroyed. * * Right before the data is destroyed, the signal interfaceAboutToBeDestroyed is emitted. * * @see release * @see interfaceAboutToBeDestroyed **/ void destroy(); /** * Setup this Shell to manage the @p shell. * When using Registry::createShell there is no need to call this * method. **/ void setup(org_kde_plasma_window_management *shell); /** * Sets the @p queue to use for creating a Surface. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a Surface. **/ EventQueue *eventQueue(); operator org_kde_plasma_window_management*(); operator org_kde_plasma_window_management*() const; /** * Whether the system is currently showing the desktop. * This means that the system focuses on the desktop and hides other windows. * @see setShowingDesktop * @see showDesktop * @see hideDesktop * @see showingDesktopChanged **/ bool isShowingDesktop() const; /** * Requests to change the showing desktop state to @p show. * @see isShowingDesktop * @see showDesktop * @see hideDesktop **/ void setShowingDesktop(bool show); /** * Same as calling setShowingDesktop with @c true. * @see setShowingDesktop **/ void showDesktop(); /** * Same as calling setShowingDesktop with @c false. * @see setShowingDesktop **/ void hideDesktop(); /** * @returns All windows currently known to the PlasmaWindowManagement * @see windowCreated **/ QList windows() const; /** * @returns The currently active PlasmaWindow, the PlasmaWindow which * returns @c true in {@link PlasmaWindow::isActive} or @c nullptr in case * there is no active window. **/ PlasmaWindow *activeWindow() const; /** * Factory method to create a PlasmaWindowModel. * @returns a new created PlasmaWindowModel **/ PlasmaWindowModel *createWindowModel(); Q_SIGNALS: /** * This signal is emitted right before the interface is released. **/ void interfaceAboutToBeReleased(); /** * This signal is emitted right before the data is destroyed. **/ void interfaceAboutToBeDestroyed(); /** * The showing desktop state changed. * @see isShowingDesktop **/ void showingDesktopChanged(bool); /** * A new @p window got created. * @see windows **/ void windowCreated(KWayland::Client::PlasmaWindow *window); /** * The active window changed. * @see activeWindow **/ void activeWindowChanged(); /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the Compositor got created by * Registry::createPlasmaWindowManagement * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the org_kde_plasma_window interface. * * A PlasmaWindow gets created by the PlasmaWindowManagement and announced through * the {@link PlasmaWindowManagement::windowCreated} signal. The PlasmaWindow encapsulates * state about a window managed by the Wayland server and allows to request state changes. * * The PlasmaWindow will be automatically deleted when the PlasmaWindow gets unmapped. * * This class is a convenient wrapper for the org_kde_plasma_window interface. * The PlasmaWindow gets created by PlasmaWindowManagement. * * @see PlasmaWindowManager **/ class KWAYLANDCLIENT_EXPORT PlasmaWindow : public QObject { Q_OBJECT public: virtual ~PlasmaWindow(); /** * Releases the org_kde_plasma_window interface. * After the interface has been released the PlasmaWindow instance is no * longer valid and can be setup with another org_kde_plasma_window interface. **/ void release(); /** * Destroys the data held by this PlasmaWindow. * 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_plasma_window interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, source, &PlasmaWindow::destroy); * @endcode * * @see release **/ void destroy(); /** * @returns @c true if managing a org_kde_plasma_window. **/ bool isValid() const; operator org_kde_plasma_window*(); operator org_kde_plasma_window*() const; /** * @returns the window title. * @see titleChanged **/ QString title() const; /** * @returns the application id which should reflect the name of a desktop file. * @see appIdChanged **/ QString appId() const; /** * @returns the id of the virtual desktop this PlasmaWindow is on * @see virtualDesktopChanged **/ quint32 virtualDesktop() const; /** * @returns Whether the window is currently the active Window. * @see activeChanged **/ bool isActive() const; /** * @returns Whether the window is fullscreen * @see fullscreenChanged **/ bool isFullscreen() const; /** * @returns Whether the window is kept above other windows. * @see keepAboveChanged **/ bool isKeepAbove() const; /** * @returns Whether the window is kept below other window * @see keepBelowChanged **/ bool isKeepBelow() const; /** * @returns Whether the window is currently minimized * @see minimizedChanged **/ bool isMinimized() const; /** * @returns Whether the window is maximized. * @see maximizedChanged **/ bool isMaximized() const; /** * @returns Whether the window is shown on all desktops. * @see virtualDesktop * @see onAllDesktopsChanged **/ bool isOnAllDesktops() const; /** * @returns Whether the window is demanding attention. * @see demandsAttentionChanged **/ bool isDemandingAttention() const; /** * @returns Whether the window can be closed. * @see closeableChanged **/ bool isCloseable() const; /** * @returns Whether the window can be maximized. * @see maximizeableChanged **/ bool isMaximizeable() const; /** * @returns Whether the window can be minimized. * @see minimizeableChanged **/ bool isMinimizeable() const; /** * @returns Whether the window can be set to fullscreen. * @see fullscreenableChanged **/ bool isFullscreenable() const; /** * @returns Whether the window should be ignored by a task bar. * @see skipTaskbarChanged **/ bool skipTaskbar() const; /** * @returns The icon of the window. * @see iconChanged **/ QIcon icon() const; /** * @returns Whether the window can be set to the shaded state. * @see isShaded * @see shadeableChanged * @since 5.22 */ bool isShadeable() const; /** * @returns Whether the window is shaded, that is reduced to the window decoration * @see shadedChanged * @since 5.22 */ bool isShaded() const; /** * @returns Whether the window can be moved. * @see movableChanged * @since 5.22 */ bool isMovable() const; /** * @returns Whether the window can be resized. * @see resizableChanged * @since 5.22 */ bool isResizable() const; /** * @returns Whether the virtual desktop can be changed. * @see virtualDesktopChangeableChanged * @since 5.22 */ bool isVirtualDesktopChangeable() const; /** * @returns The process id this window belongs to. * or 0 if unset * @since 5.35 */ quint32 pid() const; /** * Requests to activate the window. **/ void requestActivate(); /** * Requests to close the window. **/ void requestClose(); /** * Requests to start an interactive window move operation. * @since 5.22 */ void requestMove(); /** * Requests to start an interactive resize operation. * @since 5.22 */ void requestResize(); /** * Requests to send the window to virtual @p desktop. **/ void requestVirtualDesktop(quint32 desktop); /** * Requests the window at this model row index have its keep above state toggled. * @since 5.35 */ void requestToggleKeepAbove(); /** * Requests the window at this model row index have its keep below state toggled. * @since 5.35 */ void requestToggleKeepBelow(); /** * Requests the window at this model row index have its minimized state toggled. */ void requestToggleMinimized(); /** * Requests the window at this model row index have its maximized state toggled. */ void requestToggleMaximized(); /** * Sets the geometry of the taskbar entry for this window * relative to a panel in particular * @since 5.5 */ void setMinimizedGeometry(Surface *panel, const QRect &geom); /** * Remove the task geometry information for a particular panel * @since 5.5 */ void unsetMinimizedGeometry(Surface *panel); /** * Requests the window at this model row index have its shaded state toggled. * @since 5.22 */ void requestToggleShaded(); /** * An internal window identifier. * This is not a global window identifier. * This identifier does not correspond to QWindow::winId in any way. **/ quint32 internalId() const; /** * The parent window of this PlasmaWindow. * * If there is a parent window, this window is a transient window for the * parent window. If this method returns a null PlasmaWindow it means this * window is a top level window and is not a transient window. * * @see parentWindowChanged * @since 5.24 **/ QPointer parentWindow() const; /** * @returns The window geometry in absolute coordinates. * @see geometryChanged * @since 5.25 **/ QRect geometry() const; Q_SIGNALS: /** * The window title changed. * @see title **/ void titleChanged(); /** * The application id changed. * @see appId **/ void appIdChanged(); /** * The virtual desktop changed. * @see virtualDesktop **/ void virtualDesktopChanged(); /** * The window became active or inactive. * @see isActive **/ void activeChanged(); /** * The fullscreen state changed. * @see isFullscreen **/ void fullscreenChanged(); /** * The keep above state changed. * @see isKeepAbove **/ void keepAboveChanged(); /** * The keep below state changed. * @see isKeepBelow **/ void keepBelowChanged(); /** * The minimized state changed. * @see isMinimized **/ void minimizedChanged(); /** * The maximized state changed. * @see isMaximized **/ void maximizedChanged(); /** * The on all desktops state changed. * @see isOnAllDesktops **/ void onAllDesktopsChanged(); /** * The demands attention state changed. * @see isDemandingAttention **/ void demandsAttentionChanged(); /** * The closeable state changed. * @see isCloseable **/ void closeableChanged(); /** * The minimizeable state changed. * @see isMinimizeable **/ void minimizeableChanged(); /** * The maximizeable state changed. * @see isMaximizeable **/ void maximizeableChanged(); /** * The fullscreenable state changed. * @see isFullscreenable **/ void fullscreenableChanged(); /** * The skip taskbar state changed. * @see skipTaskbar **/ void skipTaskbarChanged(); /** * The window icon changed. * @see icon **/ void iconChanged(); /** * The shadeable state changed. * @see isShadeable * @since 5.22 */ void shadeableChanged(); /** * The shaded state changed. * @see isShaded * @since 5.22 */ void shadedChanged(); /** * The movable state changed. * @see isMovable * @since 5.22 */ void movableChanged(); /** * The resizable state changed. * @see isResizable * @since 5.22 */ void resizableChanged(); /** * The virtual desktop changeable state changed. * @see virtualDesktopChangeable * @since 5.22 */ void virtualDesktopChangeableChanged(); /** * The window got unmapped and is no longer available to the Wayland server. * This instance will be automatically deleted and one should connect to this * signal to perform cleanup. **/ void unmapped(); /** * This signal is emitted whenever the parent window changes. * @see parentWindow * @since 5.24 **/ void parentWindowChanged(); /** * This signal is emitted whenever the window geometry changes. * @see geometry * @since 5.25 **/ void geometryChanged(); private: friend class PlasmaWindowManagement; explicit PlasmaWindow(PlasmaWindowManagement *parent, org_kde_plasma_window *dataOffer, quint32 internalId); class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::PlasmaWindow*) #endif diff --git a/src/client/pointerconstraints.h b/src/client/pointerconstraints.h index 13bfc06..9c76d02 100644 --- a/src/client/pointerconstraints.h +++ b/src/client/pointerconstraints.h @@ -1,473 +1,471 @@ /**************************************************************************** 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 . ****************************************************************************/ #ifndef KWAYLAND_CLIENT_POINTERCONSTRAINTS_H #define KWAYLAND_CLIENT_POINTERCONSTRAINTS_H #include #include struct zwp_pointer_constraints_v1; struct zwp_locked_pointer_v1; struct zwp_confined_pointer_v1; class QPointF; namespace KWayland { namespace Client { class EventQueue; class LockedPointer; class Surface; class Region; class ConfinedPointer; class Pointer; /** * @short Wrapper for the zwp_pointer_constraints_v1 interface. * * This class provides a convenient wrapper for the zwp_pointer_constraints_v1 interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the PointerConstraints interface: * @code * PointerConstraints *c = registry->createPointerConstraints(name, version); * @endcode * * This creates the PointerConstraints and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * PointerConstraints *c = new PointerConstraints; * c->setup(registry->bindPointerConstraints(name, version)); * @endcode * * The PointerConstraints can be used as a drop-in replacement for any zwp_pointer_constraints_v1 * pointer as it provides matching cast operators. * * @see Registry * @since 5.29 **/ class KWAYLANDCLIENT_EXPORT PointerConstraints : public QObject { Q_OBJECT public: /** * Creates a new PointerConstraints. * Note: after constructing the PointerConstraints it is not yet valid and one needs * to call setup. In order to get a ready to use PointerConstraints prefer using * Registry::createPointerConstraints. **/ explicit PointerConstraints(QObject *parent = nullptr); virtual ~PointerConstraints(); /** * Setup this PointerConstraints to manage the @p pointerconstraints. * When using Registry::createPointerConstraints there is no need to call this * method. **/ void setup(zwp_pointer_constraints_v1 *pointerconstraints); /** * @returns @c true if managing a zwp_pointer_constraints_v1. **/ bool isValid() const; /** * Releases the zwp_pointer_constraints_v1 interface. * After the interface has been released the PointerConstraints instance is no * longer valid and can be setup with another zwp_pointer_constraints_v1 interface. **/ void release(); /** * Destroys the data held by this PointerConstraints. * 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_pointer_constraints_v1 interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, pointerconstraints, &PointerConstraints::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this PointerConstraints. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this PointerConstraints. **/ EventQueue *eventQueue(); /** * These values represent different lifetime semantics. They are passed * as arguments to the factory requests to specify how the constraint * lifetimes should be managed. * @see lockPointer * @see confinePointer **/ enum class LifeTime { /** * A OneShot pointer constraint will never reactivate once it has been * deactivated. **/ OneShot, /** * A persistent pointer constraint may again reactivate once it has * been deactivated. **/ Persistent }; /** * This factory method creates a LockedPointer. * * A LockedPointer lets the client request to disable movements of * the virtual pointer (i.e. the cursor), effectively locking the pointer * to a position. * * Creating a LockedPointer does not lock the pointer immediately; in the * future, when the compositor deems implementation-specific constraints * are satisfied, the pointer lock will be activated and the compositor * sends a locked event, reported by {@link LockedPointer::locked}. * * The protocol provides no guarantee that the constraints are ever * satisfied, and does not require the compositor to send an error if the * constraints cannot ever be satisfied. It is thus possible to request a * lock that will never activate. * * There may not be another pointer constraint of any kind requested or * active on the @p surface for any of the Pointer objects of the Seat of * the passed @p pointer when requesting a lock. If there is, an error will be * raised. * * The intersection of the @p region passed with this request and the input * region of the @p surface is used to determine where the pointer must be * in order for the lock to activate. It is up to the compositor whether to * warp the pointer or require some kind of user interaction for the lock * to activate. If the @p region is null the surface input region is used. * * A Surface may receive pointer focus without the lock being activated. * * Note that while a pointer is locked, the Pointer objects of the * corresponding seat will not emit any {@link Pointer::motion} signals, but * relative motion events will still be emitted via {@link RelativePointer::relativeMotion}. * Pointer axis and button events are unaffected. * * @param surface The Surface which should be constrained in pointer motion * @param pointer The Pointer object for which this LockedPointer should be created * @param region Region where to lock the pointer, if @c null the input region of the Surface is used * @param lifetime Whether the LockedPointer becomes invalid on unlocked * @param parent The parent object for the LockedPointer * @returns The factored LockedPointer **/ LockedPointer *lockPointer(Surface *surface, Pointer *pointer, Region *region, LifeTime lifetime, QObject *parent = nullptr); /** * This factory method creates a ConfinedPointer. * * A ConfinedPointer lets the client request to confine the * pointer cursor to a given @p region. Creating a ConfinedPointer * does not take effect immediately; in the future, when the compositor * deems implementation-specific constraints are satisfied, the pointer * confinement will be activated and the compositor sends a confined event, * which is reported through the {@link ConfinedPointer::confined} signal. * * The intersection of the @p region passed and the input region of the * @p surface is used to determine where the pointer must be * in order for the confinement to activate. It is up to the compositor * whether to warp the pointer or require some kind of user interaction for * the confinement to activate. If the @p region is @c null the @p surface input * region is used. * * @param surface The Surface which should be constrained in pointer motion * @param pointer The Pointer object for which this LockedPointer should be created * @param region Region where to confine the pointer, if @c null the input region of the Surface is used * @param lifetime Whether the ConfinedPointer becomes invalid on unconfined * @param parent The parent object for the ConfinedPointer * @returns The factored ConfinedPointer **/ ConfinedPointer *confinePointer(Surface *surface, Pointer *pointer, Region *region, LifeTime lifetime, QObject *parent = nullptr); operator zwp_pointer_constraints_v1*(); operator zwp_pointer_constraints_v1*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the PointerConstraints got created by * Registry::createPointerConstraints **/ void removed(); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the zwp_locked_pointer_v1 interface. * * The LockedPointer represents a locked pointer state. * * While the lock of this object is active, the Pointer objects of the * associated seat will not emit any {@link Pointer::motion} events. * * This object will send the signal locked when the lock is activated. * Whenever the lock is activated, it is guaranteed that the locked surface * will already have received pointer focus and that the pointer will be * within the region passed to the request creating this object. * * To unlock the pointer, delete the object. * * If the compositor decides to unlock the pointer the unlocked signal is * emitted. * * When unlocking, the compositor may warp the cursor position to the set * cursor position hint. If it does, it will not result in any relative * motion events emitted via {@link RelativePointer::relativeMotion}. * * If the Surface the lock was requested on is destroyed and the lock is not * yet activated, the LockedPointer object is now defunct and must be * deleted. * * @see PointerConstraints::lockedPointer * @since 5.29 **/ class KWAYLANDCLIENT_EXPORT LockedPointer : public QObject { Q_OBJECT public: virtual ~LockedPointer(); /** * Setup this LockedPointer to manage the @p lockedpointer. * When using PointerConstraints::createLockedPointer there is no need to call this * method. **/ void setup(zwp_locked_pointer_v1 *lockedpointer); /** * @returns @c true if managing a zwp_locked_pointer_v1. **/ bool isValid() const; /** * Releases the zwp_locked_pointer_v1 interface. * After the interface has been released the LockedPointer instance is no * longer valid and can be setup with another zwp_locked_pointer_v1 interface. **/ void release(); /** * Destroys the data held by this LockedPointer. * 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_locked_pointer_v1 interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, lockedpointer, &LockedPointer::destroy); * @endcode * * @see release **/ void destroy(); /** * Set the cursor position hint relative to the top left corner of the Surface. * * If the client is drawing its own cursor, it should update the position * hint to the position of its own cursor. A compositor may use this * information to warp the pointer upon unlock in order to avoid pointer * jumps. * * The cursor position hint is double buffered. The new hint will only take * effect when the associated surface gets it pending state applied. * See {@link Surface::commit} for details. * * @param surfaceLocal The new position hint in surface local coordinates * @see Surface::commit **/ void setCursorPositionHint(const QPointF &surfaceLocal); /** * Set a new region used to lock the pointer. * * The new lock region is double-buffered. The new lock region will * only take effect when the associated Surface gets its pending state * applied. See {@link Surface::commit} for details. * * @param region The new lock region. * @see Surface::commit * @see PointerConstraints::lockPointer **/ void setRegion(Region *region); operator zwp_locked_pointer_v1*(); operator zwp_locked_pointer_v1*() const; Q_SIGNALS: /** * Notification that the pointer lock of the seat's pointer is activated. * @see unlocked **/ void locked(); /** * Notification that the pointer lock of the seat's pointer is no longer * active. If this is a oneshot pointer lock (see * wp_pointer_constraints.lifetime) this object is now defunct and should * be destroyed. If this is a persistent pointer lock (see * wp_pointer_constraints.lifetime) this pointer lock may again * reactivate in the future. * @see locked **/ void unlocked(); private: friend class PointerConstraints; explicit LockedPointer(QObject *parent = nullptr); class Private; QScopedPointer d; }; /** * @short Wrapper for zwp_confined_pointer_v1 protocol * The confine pointer interface represents a confined pointer state. * * This object will send the signal 'confined' when the confinement is * activated. Whenever the confinement is activated, it is guaranteed that * the surface the pointer is confined to will already have received pointer * focus and that the pointer will be within the region passed to the request * creating this object. It is up to the compositor to decide whether this * requires some user interaction and if the pointer will warp to within the * passed region if outside. * * To unconfine the pointer, delete the object. * * If the compositor decides to unconfine the pointer the unconfined signal is * emitted. The ConfinedPointer object is at this point defunct and should * be deleted. * @see PointerConstraints::confinePointer * @since 5.29 **/ class KWAYLANDCLIENT_EXPORT ConfinedPointer : public QObject { Q_OBJECT public: virtual ~ConfinedPointer(); /** * Setup this ConfinedPointer to manage the @p confinedpointer. * When using PointerConstraints::createConfinedPointer there is no need to call this * method. **/ void setup(zwp_confined_pointer_v1 *confinedpointer); /** * @returns @c true if managing a zwp_confined_pointer_v1. **/ bool isValid() const; /** * Releases the zwp_confined_pointer_v1 interface. * After the interface has been released the ConfinedPointer instance is no * longer valid and can be setup with another zwp_confined_pointer_v1 interface. **/ void release(); /** * Destroys the data held by this ConfinedPointer. * 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_confined_pointer_v1 interface * once there is a new connection available. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, confinedpointer, &ConfinedPointer::destroy); - * @endcode + * This method is automatically invoked when the Registry which created this + * PointerConstraints gets destroyed. * * @see release **/ void destroy(); /** * Set a new region used to confine the pointer. * * The new confine region is double-buffered. The new confine region will * only take effect when the associated Surface gets its pending state * applied. See {@link Surface::commit} for details. * * If the confinement is active when the new confinement region is applied * and the pointer ends up outside of newly applied region, the pointer may * warped to a position within the new confinement region. If warped, a * {@link Pointer::motion} signal will be emitted, but no * {@link RelativePointer::relativeMotion} signal. * * The compositor may also, instead of using the new region, unconfine the * pointer. * * @param region The new confine region. * @see Surface::commit * @see PointerConstraints::confinePointer **/ void setRegion(Region *region); operator zwp_confined_pointer_v1*(); operator zwp_confined_pointer_v1*() const; Q_SIGNALS: /** * Notification that the pointer confinement of the seat's pointer is activated. * @see unconfined **/ void confined(); /** * Notification that the pointer confinement of the seat's pointer is no * longer active. If this is a oneshot pointer confinement (see * wp_pointer_constraints.lifetime) this object is now defunct and should * be destroyed. If this is a persistent pointer confinement (see * wp_pointer_constraints.lifetime) this pointer confinement may again * reactivate in the future. * @see confined **/ void unconfined(); private: friend class PointerConstraints; explicit ConfinedPointer(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/pointergestures.h b/src/client/pointergestures.h index 1efab90..d99f2c4 100644 --- a/src/client/pointergestures.h +++ b/src/client/pointergestures.h @@ -1,416 +1,415 @@ /**************************************************************************** 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 . ****************************************************************************/ #ifndef KWAYLAND_CLIENT_POINTERGESTURES_H #define KWAYLAND_CLIENT_POINTERGESTURES_H #include #include #include struct zwp_pointer_gestures_v1; struct zwp_pointer_gesture_swipe_v1; struct zwp_pointer_gesture_pinch_v1; class QSizeF; namespace KWayland { namespace Client { class EventQueue; class PointerPinchGesture; class Pointer; class Surface; class PointerSwipeGesture; /** * @short Wrapper for the zwp_pointer_gestures_v1 interface. * * This class provides a convenient wrapper for the zwp_pointer_gestures_v1 interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the PointerGestures interface: * @code * PointerGestures *c = registry->createPointerGestures(name, version); * @endcode * * This creates the PointerGestures and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * PointerGestures *c = new PointerGestures; * c->setup(registry->bindPointerGestures(name, version)); * @endcode * * The PointerGestures can be used as a drop-in replacement for any zwp_pointer_gestures_v1 * pointer as it provides matching cast operators. * * @see Registry * @see PointerSwipeGesture * @see PointerPinchGesture * @since 5.29 **/ class KWAYLANDCLIENT_EXPORT PointerGestures : public QObject { Q_OBJECT public: /** * Creates a new PointerGestures. * Note: after constructing the PointerGestures it is not yet valid and one needs * to call setup. In order to get a ready to use PointerGestures prefer using * Registry::createPointerGestures. **/ explicit PointerGestures(QObject *parent = nullptr); virtual ~PointerGestures(); /** * Setup this PointerGestures to manage the @p pointergestures. * When using Registry::createPointerGestures there is no need to call this * method. **/ void setup(zwp_pointer_gestures_v1 *pointergestures); /** * @returns @c true if managing a zwp_pointer_gestures_v1. **/ bool isValid() const; /** * Releases the zwp_pointer_gestures_v1 interface. * After the interface has been released the PointerGestures instance is no * longer valid and can be setup with another zwp_pointer_gestures_v1 interface. **/ void release(); /** * Destroys the data held by this PointerGestures. * 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_pointer_gestures_v1 interface * once there is a new connection available. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, pointergestures, &PointerGestures::destroy); + * This method is automatically invoked when the Registry which created this + * PointerGestures gets destroyed. * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this PointerGestures. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this PointerGestures. **/ EventQueue *eventQueue(); /** * Creates a PointerSwipeGesture for the given @p pointer with the @p parent. **/ PointerSwipeGesture *createSwipeGesture(Pointer *pointer, QObject *parent = nullptr); /** * Creates a PointerPinchGesture for the given @p pointer with the @p parent. **/ PointerPinchGesture *createPinchGesture(Pointer *pointer, QObject *parent = nullptr); operator zwp_pointer_gestures_v1*(); operator zwp_pointer_gestures_v1*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the PointerGestures got created by * Registry::createPointerGestures **/ void removed(); private: class Private; QScopedPointer d; }; /** * This class is a wrapper for the zwp_pointer_gesture_swipe_v1 protocol. * * A PointerSwipeGesture object notifies a client about a multi-finger swipe * gesture detected on an indirect input device such as a touchpad. * The gesture is usually initiated by multiple fingers moving in the * same direction but once initiated the direction may change. * The precise conditions of when such a gesture is detected are * implementation-dependent. * * A gesture consists of three stages: begin, update (optional) and end. * There cannot be multiple simultaneous pinch or swipe gestures on the * same pointer/seat, how compositors prevent these situations is * implementation-dependent. * * A gesture may be cancelled by the compositor or the hardware. * Clients should not consider performing permanent or irreversible * actions until the end of a gesture has been received. * * @see PointerGestures * @see PointerPinchGesture * @since 5.29 **/ class KWAYLANDCLIENT_EXPORT PointerSwipeGesture : public QObject { Q_OBJECT public: virtual ~PointerSwipeGesture(); /** * Setup this PointerSwipeGesture to manage the @p pointerswipegesture. * When using PointerGestures::createPointerSwipeGesture there is no need to call this * method. **/ void setup(zwp_pointer_gesture_swipe_v1 *pointerswipegesture); /** * @returns @c true if managing a zwp_pointer_gesture_swipe_v1. **/ bool isValid() const; /** * Releases the zwp_pointer_gesture_swipe_v1 interface. * After the interface has been released the PointerSwipeGesture instance is no * longer valid and can be setup with another zwp_pointer_gesture_swipe_v1 interface. **/ void release(); /** * Destroys the data held by this PointerSwipeGesture. * 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_pointer_gesture_swipe_v1 interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, pointerswipegesture, &PointerSwipeGesture::destroy); * @endcode * * @see release **/ void destroy(); /** * The number of fingers taking part in this gesture. * If no gesture is in progress @c 0 is returned. **/ quint32 fingerCount() const; /** * The Surface on which this gesture is performed. * If no gesture is in progress the returned pointer is null. **/ QPointer surface() const; operator zwp_pointer_gesture_swipe_v1*(); operator zwp_pointer_gesture_swipe_v1*() const; Q_SIGNALS: /** * A gesture got started. * @param serial Unique serial for this start gesture event. * @param time Timestamp in milliseconds granularity * @see updated * @see ended * @see cancelled **/ void started(quint32 serial, quint32 time); /** * A gesture got updated. * @param delta relative coordinates of the logical center of the gesture compared to the previous event * @param time Timestamp in milliseconds granularity * @see started * @see ended * @see cancelled **/ void updated(const QSizeF &delta, quint32 time); /** * A gesture ended. * * @param serial Unique serial for this end gesture event. * @param time Timestamp in milliseconds granularity * @see started * @see updated * @see cancelled **/ void ended(quint32 serial, quint32 time); /** * A gesture got cancelled by the Wayland compositor. * * @param serial Unique serial for this cancel gesture event. * @param time Timestamp in milliseconds granularity * @see started * @see updated * @see ended **/ void cancelled(quint32 serial, quint32 time); private: friend class PointerGestures; explicit PointerSwipeGesture(QObject *parent = nullptr); class Private; QScopedPointer d; }; /** * This class is a wrapper for the zwp_pointer_gesture_pinch_v1 protocol. * * A PointerPinchGesture object notifies a client about a multi-finger pinch * gesture detected on an indirect input device such as a touchpad. * The gesture is usually initiated by multiple fingers moving towards * each other or away from each other, or by two or more fingers rotating * around a logical center of gravity. The precise conditions of when * such a gesture is detected are implementation-dependent. * * A gesture consists of three stages: begin, update (optional) and end. * There cannot be multiple simultaneous pinch or swipe gestures on the * same pointer/seat, how compositors prevent these situations is * implementation-dependent. * * A gesture may be cancelled by the compositor or the hardware. * Clients should not consider performing permanent or irreversible * actions until the end of a gesture has been received. * * @see PointerGestures * @see PointerSwipeGesture * @since 5.29 **/ class KWAYLANDCLIENT_EXPORT PointerPinchGesture : public QObject { Q_OBJECT public: virtual ~PointerPinchGesture(); /** * Setup this PointerPinchGesture to manage the @p pointerpinchgesture. * When using PointerGestures::createPointerPinchGesture there is no need to call this * method. **/ void setup(zwp_pointer_gesture_pinch_v1 *pointerpinchgesture); /** * @returns @c true if managing a zwp_pointer_gesture_pinch_v1. **/ bool isValid() const; /** * Releases the zwp_pointer_gesture_pinch_v1 interface. * After the interface has been released the PointerPinchGesture instance is no * longer valid and can be setup with another zwp_pointer_gesture_pinch_v1 interface. **/ void release(); /** * Destroys the data held by this PointerPinchGesture. * 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_pointer_gesture_pinch_v1 interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, pointerpinchgesture, &PointerPinchGesture::destroy); * @endcode * * @see release **/ void destroy(); /** * The number of fingers taking part in this gesture. * If no gesture is in progress @c 0 is returned. **/ quint32 fingerCount() const; /** * The Surface on which this gesture is performed. * If no gesture is in progress the returned pointer is null. **/ QPointer surface() const; operator zwp_pointer_gesture_pinch_v1*(); operator zwp_pointer_gesture_pinch_v1*() const; Q_SIGNALS: /** * A gesture got started. * @param serial Unique serial for this start gesture event. * @param time Timestamp in milliseconds granularity * @see updated * @see ended * @see cancelled **/ void started(quint32 serial, quint32 time); /** * A gesture got updated. * @param delta relative coordinates of the logical center of the gesture compared to the previous event * @param scale an absolute scale compared to the start * @param rotation relative angle in degrees clockwise compared to the previous start or update event. * @param time Timestamp in milliseconds granularity * @see started * @see ended * @see cancelled **/ void updated(const QSizeF &delta, qreal scale, qreal rotation, quint32 time); /** * A gesture ended. * * @param serial Unique serial for this end gesture event. * @param time Timestamp in milliseconds granularity * @see started * @see updated * @see cancelled **/ void ended(quint32 serial, quint32 time); /** * A gesture got cancelled by the Wayland compositor. * * @param serial Unique serial for this cancel gesture event. * @param time Timestamp in milliseconds granularity * @see started * @see updated * @see ended **/ void cancelled(quint32 serial, quint32 time); private: friend class PointerGestures; explicit PointerPinchGesture(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/registry.cpp b/src/client/registry.cpp index a7f31a0..9b6b940 100644 --- a/src/client/registry.cpp +++ b/src/client/registry.cpp @@ -1,751 +1,754 @@ /******************************************************************** 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 "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" // Qt #include // wayland #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, 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::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::XdgShellUnstableV6, { 1, QByteArrayLiteral("zxdg_shell_v6"), &zxdg_shell_v6_interface, &Registry::xdgShellUnstableV6Announced, &Registry::xdgShellUnstableV6Removed }} }; 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 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(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) 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) #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(FakeInput) CREATE(OutputManagement) CREATE(OutputDevice) CREATE(ShadowManager) CREATE(BlurManager) CREATE(ContrastManager) CREATE(SlideManager) CREATE(DpmsManager) CREATE(ServerSideDecorationManager) CREATE2(ShmPool, Shm) #undef CREATE #undef CREATE2 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; } } 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 bb7e22f..5398222 100644 --- a/src/client/registry.h +++ b/src/client/registry.h @@ -1,1337 +1,1343 @@ /******************************************************************** 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_outputmanagement; struct org_kde_kwin_outputdevice; struct org_kde_kwin_fake_input; struct org_kde_kwin_idle; 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 xdg_shell; struct zxdg_shell_v6; struct zwp_relative_pointer_manager_v1; struct zwp_pointer_gestures_v1; struct zwp_pointer_constraints_v1; namespace KWayland { namespace Client { class Compositor; class ConnectionThread; class DataDeviceManager; class DpmsManager; class EventQueue; class FakeInput; class FullscreenShell; class OutputManagement; class OutputDevice; class Idle; 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 SubCompositor; class TextInputManager; class TextInputManagerUnstableV0; class TextInputManagerUnstableV2; class XdgShell; class RelativePointerManager; /** * @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 XdgShellUnstableV6 ///< Refers to zxdg_shell_v6 (unstable version 6), @since 5.XX }; 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_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.FIXME **/ 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; ///@} /** * @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 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); ///@} /** * 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_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); ///@} /** * @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_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); ///@} /** * 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/relativepointer.h b/src/client/relativepointer.h index 10cb4ff..f596555 100644 --- a/src/client/relativepointer.h +++ b/src/client/relativepointer.h @@ -1,238 +1,236 @@ /**************************************************************************** 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 . ****************************************************************************/ #ifndef KWAYLAND_CLIENT_RELATIVEPOINTER_H #define KWAYLAND_CLIENT_RELATIVEPOINTER_H #include #include struct zwp_relative_pointer_manager_v1; struct zwp_relative_pointer_v1; namespace KWayland { namespace Client { class EventQueue; class Pointer; class RelativePointer; /** * @short Wrapper for the zwp_relative_pointer_manager_v1 interface. * * This class provides a convenient wrapper for the zwp_relative_pointer_manager_v1 interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the RelativePointerManager interface: * @code * RelativePointerManager *c = registry->createRelativePointerManagerUnstableV1(name, version); * @endcode * * This creates the RelativePointerManager and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * RelativePointerManager *c = new RelativePointerManager; * c->setup(registry->RelativePointerManager(name, version)); * @endcode * * The RelativePointerManager can be used as a drop-in replacement for any zwp_relative_pointer_manager_v1 * pointer as it provides matching cast operators. * * @see Registry * @since 5.28 **/ class KWAYLANDCLIENT_EXPORT RelativePointerManager : public QObject { Q_OBJECT public: /** * Creates a new RelativePointerManager. * Note: after constructing the RelativePointerManager it is not yet valid and one needs * to call setup. In order to get a ready to use RelativePointerManager prefer using * Registry::createRelativePointerManagerUnstableV1. **/ explicit RelativePointerManager(QObject *parent = nullptr); virtual ~RelativePointerManager(); /** * Setup this RelativePointerManagerUnstableV1 to manage the @p relativepointermanagerunstablev1. * When using Registry::createRelativePointerManagerUnstableV1 there is no need to call this * method. **/ void setup(zwp_relative_pointer_manager_v1 *relativepointermanagerunstablev1); /** * @returns @c true if managing a zwp_relative_pointer_manager_v1. **/ bool isValid() const; /** * Releases the zwp_relative_pointer_manager_v1 interface. * After the interface has been released the RelativePointerManagerUnstableV1 instance is no * longer valid and can be setup with another zwp_relative_pointer_manager_v1 interface. **/ void release(); /** * Destroys the data held by this RelativePointerManagerUnstableV1. * 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_relative_pointer_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, relativepointermanagerunstablev1, &RelativePointerManagerUnstableV1::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this RelativePointerManagerUnstableV1. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this RelativePointerManagerUnstableV1. **/ EventQueue *eventQueue(); /** * Creates a RelativePointer for the given @p pointer. **/ RelativePointer *createRelativePointer(Pointer *pointer, QObject *parent = nullptr); operator zwp_relative_pointer_manager_v1*(); operator zwp_relative_pointer_manager_v1*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the RelativePointerManagerUnstableV1 got created by * Registry::createRelativePointerManagerUnstableV1 **/ void removed(); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the zwp_relative_pointer_v1 interface. * * The RelativePointer is an extension to the Pointer used for emitting * relative pointer events. It shares the same focus as Pointer of the same Seat * and will only emit events when it has focus. * * @since 5.28 **/ class KWAYLANDCLIENT_EXPORT RelativePointer : public QObject { Q_OBJECT public: virtual ~RelativePointer(); /** * Setup this RelativePointerUnstableV1 to manage the @p relativepointerunstablev1. * When using RelativePointerManagerUnstableV1::createRelativePointerUnstableV1 there is no need to call this * method. **/ void setup(zwp_relative_pointer_v1 *relativepointerunstablev1); /** * @returns @c true if managing a zwp_relative_pointer_v1. **/ bool isValid() const; /** * Releases the zwp_relative_pointer_v1 interface. * After the interface has been released the RelativePointerUnstableV1 instance is no * longer valid and can be setup with another zwp_relative_pointer_v1 interface. **/ void release(); /** * Destroys the data held by this RelativePointerUnstableV1. * 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_relative_pointer_v1 interface * once there is a new connection available. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, relativepointerunstablev1, &RelativePointerUnstableV1::destroy); - * @endcode + * This method is automatically invoked when the Registry which created this + * RelativePointer gets destroyed. * * @see release **/ void destroy(); operator zwp_relative_pointer_v1*(); operator zwp_relative_pointer_v1*() const; Q_SIGNALS: /** * A relative motion event. * * A relative motion is in the same dimension as regular motion events, * except they do not represent an absolute position. For example, * moving a pointer from (x, y) to (x', y') would have the equivalent * relative motion (x' - x, y' - y). If a pointer motion caused the * absolute pointer position to be clipped by for example the edge of the * monitor, the relative motion is unaffected by the clipping and will * represent the unclipped motion. * * This signal also contains non-accelerated motion deltas (@p deltaNonAccelerated). * The non-accelerated delta is, when applicable, the regular pointer motion * delta as it was before having applied motion acceleration and other * transformations such as normalization. * * Note that the non-accelerated delta does not represent 'raw' events as * they were read from some device. Pointer motion acceleration is device- * and configuration-specific and non-accelerated deltas and accelerated * deltas may have the same value on some devices. * * Relative motions are not coupled to Pointer motion events, * and can be sent in combination with such events, but also independently. There may * also be scenarios where Pointer motion is sent, but there is no * relative motion. The order of an absolute and relative motion event * originating from the same physical motion is not guaranteed. * * @param delta Motion vector * @param deltaNonAccelerated non-accelerated motion vector * @param microseconds timestamp with microseconds granularity **/ void relativeMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 timestamp); private: friend class RelativePointerManager; explicit RelativePointer(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/seat.h b/src/client/seat.h index 0240f32..62265c2 100644 --- a/src/client/seat.h +++ b/src/client/seat.h @@ -1,208 +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 . *********************************************************************/ #ifndef WAYLAND_SEAT_H #define WAYLAND_SEAT_H #include #include struct wl_seat; struct wl_touch; namespace KWayland { namespace Client { class EventQueue; class Keyboard; class Pointer; class Touch; /** * @short Wrapper for the wl_seat interface. * * This class provides a convenient wrapper for the wl_seat interface. * It's main purpose is to provide the interfaces for Keyboard, Pointer and Touch. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the Seat interface: * @code * Seat *s = registry->createSeat(name, version); * @endcode * * This creates the Seat and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * Seat *s = new Seat; * s->setup(registry->bindSeat(name, version)); * @endcode * * The Seat can be used as a drop-in replacement for any wl_seat * pointer as it provides matching cast operators. * * @see Registry * @see Keyboard * @see Pointer **/ class KWAYLANDCLIENT_EXPORT Seat : public QObject { Q_OBJECT /** * The seat has pointer devices. Default value is @c false. **/ Q_PROPERTY(bool keyboard READ hasKeyboard NOTIFY hasKeyboardChanged) /** * The seat has pointer devices. Default value is @c false. **/ Q_PROPERTY(bool pointer READ hasPointer NOTIFY hasPointerChanged) /** * The seat has touch devices. Default value is @c false. **/ Q_PROPERTY(bool touch READ hasTouch NOTIFY hasTouchChanged) /** * In a multiseat configuration this can be used by the client to help identify * which physical devices the seat represents. * Based on the seat configuration used by the compositor. **/ Q_PROPERTY(QString name READ name NOTIFY nameChanged) public: explicit Seat(QObject *parent = nullptr); virtual ~Seat(); /** * @returns @c true if managing a wl_seat. **/ bool isValid() const; /** * Setup this Seat to manage the @p seat. * When using Registry::createSeat there is no need to call this * method. **/ void setup(wl_seat *seat); /** * Releases the wl_seat interface. * After the interface has been released the Seat instance is no * longer valid and can be setup with another wl_seat interface. * * Right before the interface is released the signal interfaceAboutToBeReleased is emitted. * @see interfaceAboutToBeReleased **/ void release(); /** * Destroys the data held by this Seat. * 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_shell interface * once there is a new connection available. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, seat, &Seat::destroy); - * @endcode + * This method is automatically invoked when the Registry which created this + * Seat gets destroyed. * * Right before the data is destroyed the signal interfaceAboutToBeDestroyed is emitted. * * @see release * @see interfaceAboutToBeDestroyed **/ void destroy(); /** * Sets the @p queue to use for creating Keyboard, Pointer and Touch. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating Keyboard, Pointer and Touch. **/ EventQueue *eventQueue(); bool hasKeyboard() const; bool hasPointer() const; bool hasTouch() const; QString name() const; operator wl_seat*(); operator wl_seat*() const; /** * Creates a Keyboard. * * This method may only be called if the Seat has a keyboard. * * @param parent The parent to pass to the created Keyboard. * @returns The created Keyboard. **/ Keyboard *createKeyboard(QObject *parent = nullptr); /** * Creates a Pointer. * * This method may only be called if the Seat has a pointer. * * @param parent The parent to pass to the created Pointer. * @returns The created Pointer. **/ Pointer *createPointer(QObject *parent = nullptr); /** * Creates a Touch. * * This method may only be called if the Seat has touch support. * * @param parent The parent to pass to the created Touch. * @returns The created Touch. **/ Touch *createTouch(QObject *parent = nullptr); Q_SIGNALS: void hasKeyboardChanged(bool); void hasPointerChanged(bool); void hasTouchChanged(bool); void nameChanged(const QString &name); /** * This signal is emitted right before the interface is going to be released. **/ void interfaceAboutToBeReleased(); /** * This signal is emitted right before the data is going to be destroyed. **/ void interfaceAboutToBeDestroyed(); /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the Compositor got created by * Registry::createSeat * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/server_decoration.h b/src/client/server_decoration.h index e257c9b..e450230 100644 --- a/src/client/server_decoration.h +++ b/src/client/server_decoration.h @@ -1,256 +1,254 @@ /**************************************************************************** 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_CLIENT_SERVER_DECORATION_H #define KWAYLAND_CLIENT_SERVER_DECORATION_H #include #include struct org_kde_kwin_server_decoration_manager; struct org_kde_kwin_server_decoration; struct wl_surface; namespace KWayland { namespace Client { class EventQueue; class Surface; class ServerSideDecoration; /** * @short Wrapper for the org_kde_kwin_server_decoration_manager interface. * * This class provides a convenient wrapper for the org_kde_kwin_server_decoration_manager interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the ServerSideDecorationManager interface: * @code * ServerSideDecorationManager *c = registry->createServerSideDecorationManager(name, version); * @endcode * * This creates the ServerSideDecorationManager and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * ServerSideDecorationManager *c = new ServerSideDecorationManager; * c->setup(registry->bindServerSideDecorationManager(name, version)); * @endcode * * The ServerSideDecorationManager can be used as a drop-in replacement for any org_kde_kwin_server_decoration_manager * pointer as it provides matching cast operators. * * @see Registry * @since 5.6 **/ class KWAYLANDCLIENT_EXPORT ServerSideDecorationManager : public QObject { Q_OBJECT public: /** * Creates a new ServerSideDecorationManager. * Note: after constructing the ServerSideDecorationManager it is not yet valid and one needs * to call setup. In order to get a ready-to-use ServerSideDecorationManager prefer using * Registry::createServerSideDecorationManager. **/ explicit ServerSideDecorationManager(QObject *parent = nullptr); virtual ~ServerSideDecorationManager(); /** * Setup this ServerSideDecorationManager to manage the @p serversidedecorationmanager. * When using Registry::createServerSideDecorationManager there is no need to call this * method. **/ void setup(org_kde_kwin_server_decoration_manager *serversidedecorationmanager); /** * @returns @c true if managing a org_kde_kwin_server_decoration_manager. **/ bool isValid() const; /** * Releases the org_kde_kwin_server_decoration_manager interface. * After the interface has been released the ServerSideDecorationManager instance is no * longer valid and can be setup with another org_kde_kwin_server_decoration_manager interface. **/ void release(); /** * Destroys the data held by this ServerSideDecorationManager. * 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_manager interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, serversidedecorationmanager, &ServerSideDecorationManager::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this ServerSideDecorationManager. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this ServerSideDecorationManager. **/ EventQueue *eventQueue(); ServerSideDecoration *create(Surface *surface, QObject *parent = nullptr); ServerSideDecoration *create(wl_surface *surface, QObject *parent = nullptr); operator org_kde_kwin_server_decoration_manager*(); operator org_kde_kwin_server_decoration_manager*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the ServerSideDecorationManager got created by * Registry::createServerSideDecorationManager **/ void removed(); private: class Private; QScopedPointer d; }; /** * @brief Describing how a Surface should be decorated. * * Use ServerSideDecorationManager::create to create a ServerSideDecoration. * * @see ServerSideDecorationManager * @since 5.6 **/ class KWAYLANDCLIENT_EXPORT ServerSideDecoration : public QObject { Q_OBJECT public: virtual ~ServerSideDecoration(); /** * Setup this ServerSideDecoration to manage the @p serversidedecoration. * When using ServerSideDecorationManager::createServerSideDecoration there is no need to call this * method. **/ void setup(org_kde_kwin_server_decoration *serversidedecoration); /** * @returns @c true if managing a org_kde_kwin_server_decoration. **/ bool isValid() const; /** * Releases the org_kde_kwin_server_decoration interface. * After the interface has been released the ServerSideDecoration instance is no * longer valid and can be setup with another org_kde_kwin_server_decoration interface. **/ void release(); /** * Destroys the data held by this ServerSideDecoration. * 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 interface * once there is a new connection available. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, serversidedecoration, &ServerSideDecoration::destroy); - * @endcode + * This method is automatically invoked when the Registry which created this + * ServerDecoration gets destroyed. * * @see release **/ void destroy(); /** * Decoration mode used for the Surface. **/ enum class Mode { /** * Undecorated: neither client, nor server provide decoration. Example: popups. **/ None, /** * The decoration is part of the surface. **/ Client, /** * The surface gets embedded into a decoration frame provided by the Server. **/ Server, }; /** * Request the decoration @p mode for the Surface. * * The server will acknowledge the change which will trigger the modeChanged signal. * * @see mode * @see modeChanged **/ void requestMode(Mode mode); /** * @returns The current decoration mode for the Surface. * * The mode represents the mode pushed from the Server. * @see requestMode * @see modeChanged **/ Mode mode() const; /** * @returns The default decoration mode the server uses * * @see mode **/ Mode defaultMode() const; operator org_kde_kwin_server_decoration*(); operator org_kde_kwin_server_decoration*() const; Q_SIGNALS: /** * Emitted whenever the Server changes the decoration mode for the Surface. * @see requestMode * @see mode **/ void modeChanged(); private: friend class ServerSideDecorationManager; explicit ServerSideDecoration(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::ServerSideDecoration::Mode) #endif diff --git a/src/client/shadow.cpp b/src/client/shadow.cpp index 0c83deb..e4374da 100644 --- a/src/client/shadow.cpp +++ b/src/client/shadow.cpp @@ -1,208 +1,211 @@ /******************************************************************** 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 "shadow.h" #include "event_queue.h" #include "surface.h" #include "wayland_pointer_p.h" #include #include namespace KWayland { namespace Client { class ShadowManager::Private { public: Private() = default; WaylandPointer manager; EventQueue *queue = nullptr; }; ShadowManager::ShadowManager(QObject *parent) : QObject(parent) , d(new Private) { } ShadowManager::~ShadowManager() { release(); } void ShadowManager::release() { d->manager.release(); } void ShadowManager::destroy() { d->manager.destroy(); } bool ShadowManager::isValid() const { return d->manager.isValid(); } void ShadowManager::setup(org_kde_kwin_shadow_manager *manager) { Q_ASSERT(manager); Q_ASSERT(!d->manager); d->manager.setup(manager); } void ShadowManager::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *ShadowManager::eventQueue() { return d->queue; } Shadow *ShadowManager::createShadow(Surface *surface, QObject *parent) { Q_ASSERT(isValid()); Shadow *s = new Shadow(parent); auto w = org_kde_kwin_shadow_manager_create(d->manager, *surface); if (d->queue) { d->queue->addProxy(w); } s->setup(w); return s; } void ShadowManager::removeShadow(Surface *surface) { Q_ASSERT(isValid()); org_kde_kwin_shadow_manager_unset(d->manager, *surface); } ShadowManager::operator org_kde_kwin_shadow_manager*() { return d->manager; } ShadowManager::operator org_kde_kwin_shadow_manager*() const { return d->manager; } class Shadow::Private { public: WaylandPointer shadow; }; Shadow::Shadow(QObject *parent) : QObject(parent) , d(new Private) { } Shadow::~Shadow() { release(); } void Shadow::release() { d->shadow.release(); } void Shadow::setup(org_kde_kwin_shadow *shadow) { Q_ASSERT(shadow); Q_ASSERT(!d->shadow); d->shadow.setup(shadow); } void Shadow::destroy() { d->shadow.destroy(); } bool Shadow::isValid() const { return d->shadow.isValid(); } void Shadow::setOffsets(const QMarginsF &margins) { Q_ASSERT(isValid()); org_kde_kwin_shadow_set_left_offset(d->shadow, wl_fixed_from_double(margins.left())); org_kde_kwin_shadow_set_top_offset(d->shadow, wl_fixed_from_double(margins.top())); org_kde_kwin_shadow_set_right_offset(d->shadow, wl_fixed_from_double(margins.right())); org_kde_kwin_shadow_set_bottom_offset(d->shadow, wl_fixed_from_double(margins.bottom())); } void Shadow::commit() { Q_ASSERT(isValid()); org_kde_kwin_shadow_commit(d->shadow); } #ifndef DOXYGEN_SHOULD_SKIP_THIS #define attach( __PART__, __WAYLAND_PART__ ) \ void Shadow::attach##__PART__(wl_buffer *buffer) \ { \ Q_ASSERT(isValid()); \ org_kde_kwin_shadow_attach_##__WAYLAND_PART__(d->shadow, buffer); \ } \ void Shadow::attach##__PART__(Buffer *buffer) \ { \ + if (!buffer) {\ + return;\ + }\ attach##__PART__(buffer->buffer()); \ } \ void Shadow::attach##__PART__(Buffer::Ptr buffer) \ { \ attach##__PART__(buffer.toStrongRef().data()); \ } attach(Left, left) attach(TopLeft, top_left) attach(Top, top) attach(TopRight, top_right) attach(Right, right) attach(BottomRight, bottom_right) attach(Bottom, bottom) attach(BottomLeft, bottom_left) #undef attach #endif Shadow::operator org_kde_kwin_shadow*() { return d->shadow; } Shadow::operator org_kde_kwin_shadow*() const { return d->shadow; } } } diff --git a/src/client/shadow.h b/src/client/shadow.h index 5c61b9c..82da51a 100644 --- a/src/client/shadow.h +++ b/src/client/shadow.h @@ -1,248 +1,246 @@ /******************************************************************** 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_SHADOW_H #define KWAYLAND_SHADOW_H #include "buffer.h" #include #include #include #include struct wl_buffer; struct org_kde_kwin_shadow; struct org_kde_kwin_shadow_manager; class QMarginsF; class QWindow; namespace KWayland { namespace Client { class EventQueue; class Shadow; class Surface; /** * @short Wrapper for the org_kde_kwin_shadow_manager interface. * * This class provides a convenient wrapper for the org_kde_kwin_shadow_manager interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the ShadowManager interface: * @code * ShadowManager *s = registry->createShadowManager(name, version); * @endcode * * This creates the ShadowManager and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * ShadowManager *s = new ShadowManager; * s->setup(registry->bindShadowManager(name, version)); * @endcode * * The ShadowManager can be used as a drop-in replacement for any org_kde_kwin_shadow_manager * pointer as it provides matching cast operators. * * @see Registry * @since 5.4 **/ class KWAYLANDCLIENT_EXPORT ShadowManager : public QObject { Q_OBJECT public: /** * Creates a new ShadowManager. * Note: after constructing the ShadowManager it is not yet valid and one needs * to call setup. In order to get a ready to use ShadowManager prefer using * Registry::createShadowManager. **/ explicit ShadowManager(QObject *parent = nullptr); virtual ~ShadowManager(); /** * @returns @c true if managing a org_kde_kwin_shadow_manager. **/ bool isValid() const; /** * Setup this ShadowManager to manage the @p compositor. * When using Registry::createShadowManager there is no need to call this * method. **/ void setup(org_kde_kwin_shadow_manager *compositor); /** * Releases the org_kde_kwin_shadow_manager interface. * After the interface has been released the ShadowManager instance is no * longer valid and can be setup with another org_kde_kwin_shadow_manager interface. **/ void release(); /** * Destroys the data held by this ShadowManager. * 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_shadow_manager interface * once there is a new connection available. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, compositor, &ShadowManager::destroy); - * @endcode + * This method is automatically invoked when the Registry which created this + * Shadow gets destroyed. * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating a Shadow. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a Shadow. **/ EventQueue *eventQueue(); /** * Creates and setup a new Shadow with @p parent. * @param parent The parent to pass to the Shadow. * @returns The new created Shadow **/ Shadow *createShadow(Surface *surface, QObject *parent = nullptr); void removeShadow(Surface *surface); operator org_kde_kwin_shadow_manager*(); operator org_kde_kwin_shadow_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::createShadowManager * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the org_kde_kwin_shadow interface. * * This class is a convenient wrapper for the org_kde_kwin_shadow interface. * To create a Shadow call Compositor::createShadow. * * The main purpose of this class is to setup the next frame which * should be rendered. Therefore it provides methods to add damage * and to attach a new Buffer and to finalize the frame by calling * commit. * * @see Compositor **/ class KWAYLANDCLIENT_EXPORT Shadow : public QObject { Q_OBJECT public: virtual ~Shadow(); /** * Setup this Shadow to manage the @p shadow. * When using Compositor::createSurface there is no need to call this * method. **/ void setup(org_kde_kwin_shadow *shadow); /** * Releases the org_kde_kwin_shadow interface. * After the interface has been released the Shadow instance is no * longer valid and can be setup with another org_kde_kwin_shadow interface. **/ void release(); /** * Destroys the data held by this Shadow. * 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_shadow interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, shadow, &Shadow::destroy); * @endcode * * @see release **/ void destroy(); /** * @returns @c true if managing a org_kde_kwin_shadow. **/ bool isValid() const; void commit(); void attachLeft(wl_buffer *buffer); void attachLeft(Buffer *buffer); void attachLeft(Buffer::Ptr buffer); void attachTopLeft(wl_buffer *buffer); void attachTopLeft(Buffer *buffer); void attachTopLeft(Buffer::Ptr buffer); void attachTop(wl_buffer *buffer); void attachTop(Buffer *buffer); void attachTop(Buffer::Ptr buffer); void attachTopRight(wl_buffer *buffer); void attachTopRight(Buffer *buffer); void attachTopRight(Buffer::Ptr buffer); void attachRight(wl_buffer *buffer); void attachRight(Buffer *buffer); void attachRight(Buffer::Ptr buffer); void attachBottomRight(wl_buffer *buffer); void attachBottomRight(Buffer *buffer); void attachBottomRight(Buffer::Ptr buffer); void attachBottom(wl_buffer *buffer); void attachBottom(Buffer *buffer); void attachBottom(Buffer::Ptr buffer); void attachBottomLeft(wl_buffer *buffer); void attachBottomLeft(Buffer *buffer); void attachBottomLeft(Buffer::Ptr buffer); void setOffsets(const QMarginsF &margins); operator org_kde_kwin_shadow*(); operator org_kde_kwin_shadow*() const; private: friend class ShadowManager; explicit Shadow(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/shm_pool.h b/src/client/shm_pool.h index c625334..d6af7c7 100644 --- a/src/client/shm_pool.h +++ b/src/client/shm_pool.h @@ -1,256 +1,254 @@ /******************************************************************** 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_SHM_POOL_H #define WAYLAND_SHM_POOL_H #include #include "buffer.h" #include class QImage; class QSize; struct wl_shm; namespace KWayland { namespace Client { class EventQueue; /** * @short Wrapper class for wl_shm interface. * * This class holds a shared memory pool together with the Wayland server. * * To use this class one needs to interact with the Registry. There are two * possible ways to create a ShmPool instance: * @code * ShmPool *s = registry->createShmPool(name, version); * @endcode * * This creates the ShmPool and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * ShmPool *s = new ShmPool; * s->setup(registry->bindShm(name, version)); * @endcode * * The ShmPool holds a memory-mapped file from which it provides Buffers. * All Buffers are held by the ShmPool and can be reused. Whenever a Buffer * is requested the ShmPool tries to reuse an existing Buffer. A Buffer can * be reused if the following conditions hold * @li it's no longer marked as used * @li the server released the buffer * @li the size matches * @li the stride matches * @li the format matches * * The ownership of a Buffer stays with ShmPool. The ShmPool might destroy the * Buffer at any given time. Because of that ShmPool only provides QWeakPointer * for Buffers. Users should always check whether the pointer is still valid and * only promote to a QSharedPointer for a short time, e.g. to set new data. * * The ShmPool can provide Buffers for different purposes. One can create a Buffer * from an existing QImage. This will use a Buffer with same size, stride and image * format as the QImage and copy the content of the QImage into the Buffer. * The memory is not shared: * @code * QImage image(24, 24, QImage::Format_ARG32); * image.fill(Qt::transparent); * Buffer::Ptr buffer = s->createBuffer(image); * @endcode * * It is also possible to create a Buffer and copy the content from a generic location. * Like above this doesn't share the content but copies it: * @code * QImage image(24, 24, QImage::Format_ARG32); * image.fill(Qt::transparent); * Buffer::Ptr buffer = s->createBuffer(image.size(), image.bytesPerLine(), image.constBits()); * @endcode * * Last but not least it is possible to get a Buffer without copying content directly to it. * This means an empty area is just reserved and can be used to e.g. share the memory with a * QImage: * @code * const QSize size = QSize(24, 24); * const int stride = size.width() * 4; * Buffer::Ptr buffer = s->getBuffer(size, stride, Buffer::Format::RGB32); * if (!buffer) { * qDebug() << "Didn't get a valid Buffer"; * return; * } * QImage image(buffer.toStrongRef()->address(), size.width(), size.height(), stride, QImage::Format_RGB32); * image.fill(Qt::black); * @endcode * * A Buffer can be attached to a Surface: * @code * Compositor *c = registry.createCompositor(name, version); * Surface *s = c->createSurface(); * s->attachBuffer(buffer); * s->damage(QRect(QPoint(0, 0), size)); * @endcode * * Once a Buffer is attached to a Surface and the Surface is committed, it might be released * by the Wayland server and thus is free to be reused again. If the client code wants to * continue using the Buffer it must call Buffer::setUsed on it. This is important if the memory * is shared for example with a QImage as the memory buffer for a QImage must remain valid * throughout the life time of the QImage: * @code * buffer.toStrongRef()->setUsed(true); * @endcode * * This is also important for the case that the shared memory pool needs to be resized. * The ShmPool will automatically resize if it cannot provide a new Buffer. During the resize * all existing Buffers are unmapped and any shared objects must be recreated. The ShmPool emits * the signal poolResized() after the pool got resized. * * @see Buffer **/ class KWAYLANDCLIENT_EXPORT ShmPool : public QObject { Q_OBJECT public: explicit ShmPool(QObject *parent = nullptr); virtual ~ShmPool(); /** * @returns @c true if the ShmPool references a wl_shm interface and the shared memory pool * is setup. **/ bool isValid() const; /** * Setup this ShmPool to manage the @p shm. * This also creates the shared memory pool. * When using Registry::createShmPool there is no need to call this * method. **/ void setup(wl_shm *shm); /** * Releases the wl_shm interface. * After the interface has been released the ShmPool instance is no * longer valid and can be setup with another wl_shm interface. * * This also destroys the shared memory pool and all Buffers are destroyed. **/ void release(); /** * Destroys the data held by this ShmPool. * 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_shm interface * once there is a new connection available. * * All Buffers are destroyed! * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, shmPool, &ShmPool::destroy); - * @endcode + * This method is automatically invoked when the Registry which created this + * ShmPool gets destroyed. * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating a Buffer. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a Buffer. **/ EventQueue *eventQueue(); /** * Provides a Buffer with: * @li same size as @p image * @li same stride as @p image * @li same format as @p image * * If the ShmPool fails to provide such a Buffer a @c null Buffer::Ptr is returned. * The content of the @p image is copied into the buffer. The @p image and * returned Buffer do not share memory. * * @param image The image which should be copied into the Buffer * @return Buffer with copied content of @p image in success case, a @c null Buffer::Ptr otherwise * @see getBuffer **/ Buffer::Ptr createBuffer(const QImage &image); /** * Provides a Buffer with @p size, @p stride and @p format. * * If the ShmPool fails to provide such a Buffer a @c null Buffer::Ptr is returned. * A memory copy is performed from @p src into the Buffer. The Buffer does not share * memory with @p src. * * @param size The requested size for the Buffer * @param stride The requested stride for the Buffer * @param src The source memory location to copy from * @param format The requested format for the Buffer * @return Buffer with copied content of @p src in success case, a @c null Buffer::Ptr otherwise * @see getBuffer **/ Buffer::Ptr createBuffer(const QSize &size, int32_t stride, const void *src, Buffer::Format format = Buffer::Format::ARGB32); void *poolAddress() const; /** * Provides a Buffer with @p size, @p stride and @p format. * * If the ShmPool fails to provide such a Buffer a @c null Buffer::Ptr is returned. * Unlike with createBuffer there is no memory copy performed. This provides a bare Buffer * to be used by the user. * * @param size The requested size for the Buffer * @param stride The requested stride for the Buffer * @param format The requested format for the Buffer * @return Buffer as requested in success case, a @c null Buffer::Ptr otherwise. * @see createBuffer **/ Buffer::Ptr getBuffer(const QSize &size, int32_t stride, Buffer::Format format = Buffer::Format::ARGB32); wl_shm *shm(); Q_SIGNALS: /** * This signal is emitted whenever the shared memory pool gets resized. * Any used Buffer must be remapped. **/ void poolResized(); /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the Compositor got created by * Registry::createShmPool * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/slide.h b/src/client/slide.h index 7e2a211..3c56443 100644 --- a/src/client/slide.h +++ b/src/client/slide.h @@ -1,226 +1,224 @@ /**************************************************************************** Copyright 2015 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 . ****************************************************************************/ #ifndef KWAYLAND_CLIENT_SLIDE_H #define KWAYLAND_CLIENT_SLIDE_H #include #include struct org_kde_kwin_slide_manager; struct org_kde_kwin_slide; namespace KWayland { namespace Client { class EventQueue; class Slide; class Surface; /** * @short Wrapper for the org_kde_kwin_slide_manager interface. * * This class provides a convenient wrapper for the org_kde_kwin_slide_manager interface. * * Ask the compositor to move the surface from a location * to another with a slide animation. * * The from argument provides a clue about where the slide * animation begins, offset is the distance from screen * edge to begin the animation. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the SlideManager interface: * @code * SlideManager *c = registry->createSlideManager(name, version); * @endcode * * This creates the SlideManager and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * SlideManager *c = new SlideManager; * c->setup(registry->bindSlideManager(name, version)); * @endcode * * The SlideManager can be used as a drop-in replacement for any org_kde_kwin_slide_manager * pointer as it provides matching cast operators. * * @see Registry **/ class KWAYLANDCLIENT_EXPORT SlideManager : public QObject { Q_OBJECT public: /** * Creates a new SlideManager. * Note: after constructing the SlideManager it is not yet valid and one needs * to call setup. In order to get a ready to use SlideManager prefer using * Registry::createSlideManager. **/ explicit SlideManager(QObject *parent = nullptr); virtual ~SlideManager(); /** * Setup this SlideManager to manage the @p slidemanager. * When using Registry::createSlideManager there is no need to call this * method. **/ void setup(org_kde_kwin_slide_manager *slidemanager); /** * @returns @c true if managing a org_kde_kwin_slide_manager. **/ bool isValid() const; /** * Releases the org_kde_kwin_slide_manager interface. * After the interface has been released the SlideManager instance is no * longer valid and can be setup with another org_kde_kwin_slide_manager interface. **/ void release(); /** * Destroys the data held by this SlideManager. * 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_slide_manager interface * once there is a new connection available. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, slidemanager, &SlideManager::destroy); - * @endcode + * This method is automatically invoked when the Registry which created this + * SlideManager gets destroyed. * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this SlideManager. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this SlideManager. **/ EventQueue *eventQueue(); Slide *createSlide(Surface *surface, QObject *parent = nullptr); void removeSlide(Surface *surface); operator org_kde_kwin_slide_manager*(); operator org_kde_kwin_slide_manager*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the SlideManager got created by * Registry::createSlideManager **/ void removed(); private: class Private; QScopedPointer d; }; /** * TODO */ class KWAYLANDCLIENT_EXPORT Slide : public QObject { Q_OBJECT public: enum Location { Left = 0, /**< Slide from the left edge of the screen */ Top, /**< Slide from the top edge of the screen */ Right, /**< Slide from the bottom edge of the screen */ Bottom /**< Slide from the bottom edge of the screen */ }; virtual ~Slide(); /** * Setup this Slide to manage the @p slide. * When using SlideManager::createSlide there is no need to call this * method. **/ void setup(org_kde_kwin_slide *slide); /** * @returns @c true if managing a org_kde_kwin_slide. **/ bool isValid() const; /** * Releases the org_kde_kwin_slide interface. * After the interface has been released the Slide instance is no * longer valid and can be setup with another org_kde_kwin_slide interface. **/ void release(); /** * Destroys the data held by this Slide. * 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_slide interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, slide, &Slide::destroy); * @endcode * * @see release **/ void destroy(); void commit(); /** * Set the location of the screen to slide the window from */ void setLocation(Slide::Location location); /** * Set the offset from the screen edge * to make the window slide from */ void setOffset(qint32 offset); operator org_kde_kwin_slide*(); operator org_kde_kwin_slide*() const; private: friend class SlideManager; explicit Slide(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/surface.h b/src/client/surface.h index e58a8ca..5d2d912 100644 --- a/src/client/surface.h +++ b/src/client/surface.h @@ -1,300 +1,298 @@ /******************************************************************** 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_SURFACE_H #define WAYLAND_SURFACE_H #include "buffer.h" #include #include #include #include #include struct wl_buffer; struct wl_surface; class QWindow; namespace KWayland { namespace Client { class Output; class Region; /** * @short Wrapper for the wl_surface interface. * * This class is a convenient wrapper for the wl_surface interface. * To create a Surface call Compositor::createSurface. * * The main purpose of this class is to setup the next frame which * should be rendered. Therefore it provides methods to add damage * and to attach a new Buffer and to finalize the frame by calling * commit. * * @see Compositor **/ class KWAYLANDCLIENT_EXPORT Surface : public QObject { Q_OBJECT public: explicit Surface(QObject *parent = nullptr); virtual ~Surface(); /** * Creates a Surface for the given @p window. * This is an integration feature for QtWayland. On non-wayland platforms this method returns * @c nullptr as well as for not created QWindows. * * The returned Surface will be fully setup, but won't be released. It gets automatically * destroyed together with the @p window. * @since 5.4 **/ static Surface *fromWindow(QWindow *window); /** * Creates a Surface for the given @p winId. * This is an integration feature for QtWayland. On non-wayland platforms this method returns * @c nullptr as well as for not created QWindows. * * The returned Surface will be fully setup, but won't be released. It gets automatically * destroyed together with the QWindow corresponding * the @p wid. * @since 5.5 **/ static Surface *fromQtWinId(WId wid); /** * Setup this Surface to manage the @p surface. * When using Compositor::createSurface there is no need to call this * method. **/ void setup(wl_surface *surface); /** * Releases the wl_surface interface. * After the interface has been released the Surface instance is no * longer valid and can be setup with another wl_surface interface. **/ void release(); /** * Destroys the data held by this Surface. * 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_surface interface * once there is a new connection available. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, surface, &Surface::destroy); - * @endcode + * This method is automatically invoked when the Registry which created this + * Surface gets destroyed. * * @see release **/ void destroy(); /** * @returns @c true if managing a wl_surface. **/ bool isValid() const; /** * Registers a frame rendered callback. * This registers a callback in the Wayland server to be notified once the * next frame for this Surface has been rendered. The Surface will emit the * signal frameRendered after receiving the callback from the server. * * Instead of using this method one should prefer using the CommitFlag::FrameCallback * in commit. This method is intended for cases when the Surface is going to * be committed on other ways, e.g. through the OpenGL/EGL stack. * * @see frameRendered * @see commit **/ void setupFrameCallback(); /** * Flags to be added to commit. * @li None: no flag * @li FrameCallback: register a frame rendered callback * * Instead of setting the FrameCallback flag one can also call * setupFrameCallback. If one uses setupFrameCallback one may not * use the FrameCallback flag when committing the Surface. * * @see commit * @see setupFrameCallback **/ enum class CommitFlag { None, FrameCallback }; void commit(CommitFlag flag = CommitFlag::FrameCallback); /** * Mark @p rect as damaged for the next frame. **/ void damage(const QRect &rect); /** * Mark @p region as damaged for the next frame. **/ void damage(const QRegion ®ion); /** * Attaches the @p buffer to this Surface for the next frame. * @param buffer The buffer to attach to this Surface * @param offset Position of the new upper-left corner in relation to previous frame **/ void attachBuffer(wl_buffer *buffer, const QPoint &offset = QPoint()); /** * Overloaded method for convenience. **/ void attachBuffer(Buffer *buffer, const QPoint &offset = QPoint()); /** * Overloaded method for convenience. **/ void attachBuffer(Buffer::Ptr buffer, const QPoint &offset = QPoint()); /** * Sets the input region to @p region. * * This is a double buffered state and will be applied with the next Surface * commit. Initially the Surface is set up to an infinite input region. * By passing @c null as the input region, it gets reset to an infinite input * region. * * Note: the Region is being copied and can be destroyed directly after passing * to this method. * * @param region The new input region or an infinite region if @c null * @see commit **/ void setInputRegion(const Region *region = nullptr); /** * Sets the opaque region to @p region. * * This is a double buffered state and will be applied with the next Surface * commit. Initially the Surface is set up to an empty opaque region. * By passing @c null as the opaque region, it gets reset to an empty opaque * region. * * Note: the Region is being copied and can be destroyed directly after passing * to this method. * * @param region The new opaque region or an empty region if @c null * @see commit **/ void setOpaqueRegion(const Region *region = nullptr); void setSize(const QSize &size); QSize size() const; /** * The purpose of this method is to allow to supply higher resolution buffer data for use * on high resolution outputs. It's intended that the same buffer scale as the scale of the * output that the surface is displayed on is used. * This means the compositor can avoid scaling when rendering the surface on that output. * * Note that if @p scale is larger than 1 you have to attach a buffer that is larger * (by a factor of scale in each dimension) than the desired surface size. * * The default scale factor is 1. * * The state is only applied with the next commit. * * @see scale * @see commit * @since 5.22 **/ void setScale(qint32 scale); /** * @returns The current scale factor, if not explicitly set it's @c 1. * @see setScale * @since 5.22 **/ qint32 scale() const; operator wl_surface*(); operator wl_surface*() const; /** * @returns the id of the referenced wl_proxy. * @since 5.4 **/ quint32 id() const; /** * @returns All Outputs the Surface is on, may be none. * @see outputEntered * @see outputLeft * @since 5.27 **/ QVector outputs() const; /** * All Surfaces which are currently created. * TODO: KF6 return QList instead of const-ref **/ static const QList &all(); // krazy:exclude=constref /** * @returns The Surface referencing the @p native wl_surface or @c null if there is no such Surface. **/ static Surface *get(wl_surface *native); Q_SIGNALS: /** * Emitted when the server indicates that the last committed frame has been rendered. * The signal will only be emitted if a callback has been registered by either calling * setupFrameCallback or calling commit with the CommitFlag::FrameCallback. * @see setupFrameCallback * @see commit **/ void frameRendered(); void sizeChanged(const QSize&); /** * Emitted whenever a change in the Surface (e.g. creation, movement, resize) results in * a part of the Surface being within the scanout region of the Output @p o. * * @param o The Output the Surface intersects with * @see outputLeft * @see outputs * @since 5.27 **/ void outputEntered(KWayland::Client::Output *o); /** * Emitted whenever a change in the Surface (e.g. creation, movement, resize, unmapping) * results in the Surface no longer being within the scanout region of the Output @p o. * * @param o The Output the Surface no longer intersects with * @see outputEntered * @see outputs * @since 5.27 **/ void outputLeft(KWayland::Client::Output *o); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/textinput.h b/src/client/textinput.h index fbf2960..8ccf499 100644 --- a/src/client/textinput.h +++ b/src/client/textinput.h @@ -1,536 +1,534 @@ /**************************************************************************** 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 . ****************************************************************************/ #ifndef KWAYLAND_CLIENT_TEXTINPUT_H #define KWAYLAND_CLIENT_TEXTINPUT_H #include #include struct wl_text_input; struct wl_text_input_manager; struct zwp_text_input_manager_v2; namespace KWayland { namespace Client { class EventQueue; class TextInputUnstableV0; class Surface; class Seat; /** * @brief TextInput represents a Wayland interface for text input. * * The TextInput allows to have text composed by the Compositor and be sent to * the client. * * Depending on the interface the TextInputManager got created for this class * encapsulates one of the following interfaces: * @li wl_text_input * @li zwp_text_input_v2 * * @since 5.23 **/ class KWAYLANDCLIENT_EXPORT TextInput : public QObject { Q_OBJECT public: virtual ~TextInput(); /** * @returns @c true if managing a resource. **/ bool isValid() const; /** * @returns The Surface which has the text input focus on this TextInput. * @see entered * @see left **/ Surface *enteredSurface() const; void setEventQueue(EventQueue *queue); EventQueue *eventQueue() const; /** * @returns whether the input panel (virtual keyboard) is currently visible on the screen * @see inputPanelStateChanged **/ bool isInputPanelVisible() const; /** * Enable text input in a @p surface (usually when a text entry inside of it has focus). * * This can be called before or after a surface gets text (or keyboard) focus via the * enter event. Text input to a surface is only active when it has the current * text (or keyboard) focus and is enabled. * @see deactivate **/ void enable(Surface *surface); /** * Disable text input in a @p surface (typically when there is no focus on any * text entry inside the surface). * @see enable **/ void disable(Surface *surface); /** * Requests input panels (virtual keyboard) to show. * @see hideInputPanel **/ void showInputPanel(); /** * Requests input panels (virtual keyboard) to hide. * @see showInputPanel **/ void hideInputPanel(); /** * Should be called by an editor widget when the input state should be * reset, for example after the text was changed outside of the normal * input method flow. **/ void reset(); /** * Sets the plain surrounding text around the input position. * * @param text The text surrounding the cursor position * @param cursor Index in the text describing the cursor position * @param anchor Index of the selection anchor, if no selection same as cursor **/ void setSurroundingText(const QString &text, quint32 cursor, quint32 anchor); /** * The possible states for a keyEvent. * @see keyEvent **/ enum class KeyState { Pressed, Released }; /** * ContentHint allows to modify the behavior of the text input. **/ enum class ContentHint : uint32_t { /** * no special behaviour */ None = 0, /** * suggest word completions */ AutoCompletion = 1 << 0, /** * suggest word corrections */ AutoCorrection = 1 << 1, /** * switch to uppercase letters at the start of a sentence */ AutoCapitalization = 1 << 2, /** * prefer lowercase letters */ LowerCase = 1 << 3, /** * prefer uppercase letters */ UpperCase = 1 << 4, /** * prefer casing for titles and headings (can be language dependent) */ TitleCase = 1 << 5, /** * characters should be hidden */ HiddenText = 1 << 6, /** * typed text should not be stored */ SensitiveData = 1 << 7, /** * just latin characters should be entered */ Latin = 1 << 8, /** * the text input is multi line */ MultiLine = 1 << 9 }; Q_DECLARE_FLAGS(ContentHints, ContentHint) /** * The ContentPurpose allows to specify the primary purpose of a text input. * * This allows an input method to show special purpose input panels with * extra characters or to disallow some characters. */ enum class ContentPurpose : uint32_t { /** * default input, allowing all characters */ Normal, /** * allow only alphabetic characters **/ Alpha, /** * allow only digits */ Digits, /** * input a number (including decimal separator and sign) */ Number, /** * input a phone number */ Phone, /** * input an URL */ Url, /** * input an email address **/ Email, /** * input a name of a person */ Name, /** * input a password */ Password, /** * input a date */ Date, /** * input a time */ Time, /** * input a date and time */ DateTime, /** * input for a terminal */ Terminal }; /** * Sets the content @p purpose and content @p hints. * While the @p purpose is the basic purpose of an input field, the @p hints flags allow * to modify some of the behavior. **/ void setContentType(ContentHints hints, ContentPurpose purpose); /** * Sets the cursor outline @p rect in surface local coordinates. * * Allows the compositor to e.g. put a window with word suggestions * near the cursor. **/ void setCursorRectangle(const QRect &rect); /** * Sets a specific @p language. * * This allows for example a virtual keyboard to show a language specific layout. * The @p language argument is a RFC-3066 format language tag. **/ void setPreferredLanguage(const QString &language); /** * The text direction of input text. * * It is mainly needed for showing input cursor on correct side of the * editor when there is no input yet done and making sure neutral * direction text is laid out properly. * @see textDirectionChnaged **/ Qt::LayoutDirection textDirection() const; /** * The language of the input text. * * As long as the server has not emitted the language, the code will be empty. * * @returns a RFC-3066 format language tag in utf-8. * @see languageChanged **/ QByteArray language() const; /** * The cursor position inside the {@link composingText} (as byte offset) relative * to the start of the {@link composingText}. * If index is a negative number no cursor is shown. * @see composingText * @see composingTextChanged **/ qint32 composingTextCursorPosition() const; /** * The currently being composed text around the {@link composingTextCursorPosition}. * @see composingTextCursorPosition * @see composingTextChanged **/ QByteArray composingText() const; /** * The fallback text can be used to replace the {@link composingText} in some cases * (for example when losing focus). * * @see composingText * @see composingTextChanged **/ QByteArray composingFallbackText() const; /** * The commit text to be inserted. * * The commit text might be empty if only text should be deleted or the cursor be moved. * @see cursorPosition * @see anchorPosition * @see deleteSurroundingText * @see committed **/ QByteArray commitText() const; /** * The cursor position in bytes at which the {@link commitText} should be inserted. * @see committed **/ qint32 cursorPosition() const; /** * The text between anchorPosition and {@link cursorPosition} should be selected. * @see cursorPosition * @see committed **/ qint32 anchorPosition() const; /** * Holds the length before and after the cursor position to be deleted. **/ struct DeleteSurroundingText { quint32 beforeLength; quint32 afterLength; }; /** * @returns The lenght in bytes which should be deleted around the cursor position * @see committed **/ DeleteSurroundingText deleteSurroundingText() const; Q_SIGNALS: /** * Emitted whenever a Surface is focused on this TextInput. * @see enteredSurface * @see left **/ void entered(); /** * Emitted whenever a Surface loses the focus on this TextInput. * @see enteredSurface * @see entered **/ void left(); /** * Emitted whenever the state of the input panel (virtual keyboard changes). * @see isInputPanelVisible **/ void inputPanelStateChanged(); /** * Emitted whenver the text direction changes. * @see textDirection **/ void textDirectionChanged(); /** * Emitted whenever the language changes. * @see language **/ void languageChanged(); /** * Emitted when a key event was sent. * Key events are not used for normal text input operations, but for specific key symbols * which are not composable through text. * * @param xkbKeySym The XKB key symbol, not a key code * @param state Whether the event represents a press or release event * @param modifiers The hold modifiers on this event * @param time Timestamp of this event **/ void keyEvent(quint32 xkbKeySym, KWayland::Client::TextInput::KeyState state, Qt::KeyboardModifiers modifiers, quint32 time); /** * Emitted whenever the composing text and related states changed. * @see composingText * @see composingTextCursorPosition * @see composingFallbackText **/ void composingTextChanged(); /** * Emitted when the currently composing text got committed. * The {@link commitText} should get inserted at the {@link cursorPosition} and * the text around {@link deleteSurroundingText} should be deleted. * * @see commitText * @see cursorPosition * @see anchorPosition * @see deleteSurroundingText **/ void committed(); protected: class Private; QScopedPointer d; explicit TextInput(Private *p, QObject *parent = nullptr); }; /** * @brief Manager class for the TextInputManager interfaces. * * The TextInputManager supports multiple interfaces: * @li wl_text_input_manager * @li zwp_text_input_manager_v2 * * Due to that it is different to other manager classes. It can only be created through * the corresponding factory method in Registry. A manual setup is not directly possible. * * The only task of a TextInputManager is to create TextInput for a given Seat. * * @since 5.23 **/ class KWAYLANDCLIENT_EXPORT TextInputManager : public QObject { Q_OBJECT public: virtual ~TextInputManager(); /** * Setup this TextInputManager to manage the @p textinputmanagerunstablev0. * When using Registry::createTextInputManager there is no need to call this * method. **/ void setup(wl_text_input_manager *textinputmanagerunstablev0); /** * Setup this TextInputManager to manage the @p textinputmanagerunstablev0. * When using Registry::createTextInputManager there is no need to call this * method. **/ void setup(zwp_text_input_manager_v2 *textinputmanagerunstablev2); /** * @returns @c true if managing a resource. **/ bool isValid() const; /** * Releases the interface. * After the interface has been released the TextInputManager instance is no * longer valid and can be setup with another interface. **/ void release(); /** * Destroys the data held by this TextInputManager. * 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 interface * once there is a new connection available. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, textinputmanager, &TextInputManager::destroy); - * @endcode + * This method is automatically invoked when the Registry which created this + * TextInput gets destroyed. * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this TextInputManager. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this TextInputManager. **/ EventQueue *eventQueue(); /** * Creates a TextInput for the @p seat. * * @param seat The Seat to create the TextInput for * @param parent The parent to use for the TextInput **/ TextInput *createTextInput(Seat *seat, QObject *parent = nullptr); /** * @returns @c null if not for a wl_text_input_manager **/ operator wl_text_input_manager*(); /** * @returns @c null if not for a wl_text_input_manager **/ operator wl_text_input_manager*() const; /** * @returns @c null if not for a zwp_text_input_manager_v2 **/ operator zwp_text_input_manager_v2*(); /** * @returns @c null if not for a zwp_text_input_manager_v2 **/ operator zwp_text_input_manager_v2*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the TextInputManager got created by * Registry::createTextInputManager **/ void removed(); protected: class Private; explicit TextInputManager(Private *p, QObject *parent = nullptr); QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::TextInput::KeyState) Q_DECLARE_METATYPE(KWayland::Client::TextInput::ContentHint) Q_DECLARE_METATYPE(KWayland::Client::TextInput::ContentPurpose) Q_DECLARE_METATYPE(KWayland::Client::TextInput::ContentHints) Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Client::TextInput::ContentHints) #endif diff --git a/src/client/xdgshell.h b/src/client/xdgshell.h index d0d08f7..728c02b 100644 --- a/src/client/xdgshell.h +++ b/src/client/xdgshell.h @@ -1,590 +1,588 @@ /**************************************************************************** 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 . ****************************************************************************/ #ifndef KWAYLAND_CLIENT_XDG_SHELL_V5_H #define KWAYLAND_CLIENT_XDG_SHELL_V5_H #include #include #include #include struct xdg_shell; struct xdg_surface; struct xdg_popup; struct zxdg_shell_v6; struct zxdg_toplevel_v6; struct zxdg_surface_v6; struct zxdg_popup_v6; struct zxdg_position_v6; namespace KWayland { namespace Client { class EventQueue; class Output; class Surface; class Seat; class XdgShellPopup; class XdgShellSurface; /* * Builder class describing how a popup should be positioned * when created * * @since 5.XDGMERGE_VERSION */ class KWAYLANDCLIENT_EXPORT XdgPositioner { public: /* * Flags describing how a popup should be reposition if constrained */ enum class Constraint { /* * Slide the popup on the X axis until there is room */ SlideX = 1 << 0, /* * Slide the popup on the Y axis until there is room */ SlideY = 1 << 1, /* * Invert the anchor and gravity on the X axis */ FlipX = 1 << 2, /* * Invert the anchor and gravity on the Y axis */ FlipY = 1 << 3, /* * Resize the popup in the X axis */ ResizeX = 1 << 4, /* * Resize the popup in the Y axis */ ResizeY = 1 << 5 }; Q_DECLARE_FLAGS(Constraints, Constraint) XdgPositioner(const QSize &initialSize = QSize(), const QRect &anchor = QRect()); XdgPositioner(const XdgPositioner &other); ~XdgPositioner(); /* * Which edge of the anchor should the popup be positioned around */ Qt::Edges anchorEdge() const; void setAnchorEdge(Qt::Edges edge); /* * Specifies in what direction the popup should be positioned around the anchor * i.e if the gravity is "bottom", then then the top of top of the poup will be at the anchor edge * if the gravity is top, then the bottom of the popup will be at the anchor edge * */ Qt::Edges gravity() const; void setGravity(Qt::Edges edge); /* * The area this popup should be positioned around */ QRect anchorRect() const; void setAnchorRect(const QRect &anchor); /* * The size of the surface that is to be positioned. */ QSize initialSize() const; void setInitialSize(const QSize &size); /* * Specifies how the compositor should position the popup if it does not fit in the requested position */ Constraints constraints() const; void setConstraints(Constraints constaints); /* * An additional offset that should be applied from the anchor. */ QPoint anchorOffset() const; void setAnchorOffset(const QPoint &offset); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the xdg_shell interface. * * This class provides a convenient wrapper for the xdg_shell interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the XdgShell interface: * @code * XdgShell *c = registry->createXdgShell(name, version); * @endcode * * This creates the XdgShell and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * XdgShell *c = new XdgShell; * c->setup(registry->bindXdgShell(name, version)); * @endcode * * The XdgShell can be used as a drop-in replacement for any xdg_shell * pointer as it provides matching cast operators. * * @see Registry * @since 5.25 **/ class KWAYLANDCLIENT_EXPORT XdgShell : public QObject { Q_OBJECT public: virtual ~XdgShell(); /** * Setup this XdgShell to manage the @p xdgshellv5. * When using Registry::createXdgShell there is no need to call this * method. **/ void setup(xdg_shell *xdgshellv5); void setup(zxdg_shell_v6 *xdgshellv6); /** * @returns @c true if managing a xdg_shell. **/ bool isValid() const; /** * Releases the xdg_shell interface. * After the interface has been released the XdgShell instance is no * longer valid and can be setup with another xdg_shell interface. **/ void release(); /** * Destroys the data held by this XdgShell. * 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 xdg_shell interface * once there is a new connection available. * - * It is suggested to connect this method to ConnectionThread::connectionDied: - * @code - * connect(connection, &ConnectionThread::connectionDied, xdgshellv5, &XdgShell::destroy); - * @endcode + * This method is automatically invoked when the Registry which created this + * XdgShell gets destroyed. * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this XdgShell. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this XdgShell. **/ EventQueue *eventQueue(); /** * Creates a new XdgShellSurface for the given @p surface. **/ XdgShellSurface *createSurface(Surface *surface, QObject *parent = nullptr); /** * Creates a new XdgShellPopup for the given @p surface on top of @p parentSurface. * @deprecated This method is only valid for v5 **/ XdgShellPopup *createPopup(Surface *surface, Surface *parentSurface, Seat *seat, quint32 serial, const QPoint &parentPos, QObject *parent = nullptr); /** * Creates a new XdgShellPopup for the given @p surface on top of @p parentSurface with the given @p positioner. **/ XdgShellPopup *createPopup(Surface *surface, XdgShellSurface *parentSurface, const XdgPositioner &positioner, QObject *parent = nullptr); /** * Creates a new XdgShellPopup for the given @p surface on top of @p parentSurface with the given @p positioner. **/ XdgShellPopup *createPopup(Surface *surface, XdgShellPopup *parentSurface, const XdgPositioner &positioner, QObject *parent = nullptr); operator xdg_shell*(); operator xdg_shell*() const; operator zxdg_shell_v6*(); operator zxdg_shell_v6*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the XdgShell got created by * Registry::createXdgShell **/ void removed(); protected: /** * Creates a new XdgShell. * Note: after constructing the XdgShell it is not yet valid and one needs * to call setup. In order to get a ready to use XdgShell prefer using * Registry::createXdgShell. **/ class Private; explicit XdgShell(Private *p, QObject *parent = nullptr); private: QScopedPointer d; }; /** * * @since 5.25 **/ class KWAYLANDCLIENT_EXPORT XdgShellSurface : public QObject { Q_OBJECT public: virtual ~XdgShellSurface(); /** * States the Surface can be in **/ enum class State { /** * The Surface is maximized. **/ Maximized = 1 << 0, /** * The Surface is fullscreen. **/ Fullscreen = 1 << 1, /** * The Surface is currently being resized by the Compositor. **/ Resizing = 1 << 2, /** * The Surface is considered active. Does not imply keyboard focus. **/ Activated = 1 << 3 }; Q_DECLARE_FLAGS(States, State) /** * Setup this XdgShellSurface to manage the @p xdgsurfacev5. * When using XdgShell::createXdgShellSurface there is no need to call this * method. **/ void setup(xdg_surface *xdgsurfacev5); void setup(zxdg_surface_v6 *xdgsurfacev6, zxdg_toplevel_v6 *toplevel); /** * @returns @c true if managing a xdg_surface. **/ bool isValid() const; /** * Releases the xdg_surface interface. * After the interface has been released the XdgShellSurface instance is no * longer valid and can be setup with another xdg_surface interface. **/ void release(); /** * Destroys the data held by this XdgShellSurface. * 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 xdg_surface interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, xdgsurfacev5, &XdgShellSurface::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for bound proxies. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for bound proxies. **/ EventQueue *eventQueue(); /** * The currently configured size. * @see sizeChanged * @see setSize **/ QSize size() const; /** * Sets the size for the XdgShellSurface to @p size. * This is mostly an internal information. The actual size of the XdgShellSurface is * determined by the size of the Buffer attached to the XdgShellSurface's Surface. * * @param size The new size to be used for the XdgShellSurface * @see size * @see sizeChanged **/ void setSize(const QSize &size); /** * Set this XdgShellSurface as transient for @p parent. **/ void setTransientFor(XdgShellSurface *parent); /** * Sets the window title of this XdgShellSurface to @p title. **/ void setTitle(const QString &title); /** * Set an application identifier for the surface. **/ void setAppId(const QByteArray &appId); /** * Requests to show the window menu at @p pos in surface coordinates. **/ void requestShowWindowMenu(Seat *seat, quint32 serial, const QPoint &pos); /** * Requests a move on the given @p seat after the pointer button press with the given @p serial. * * @param seat The seat on which to move the window * @param serial The serial of the pointer button press which should trigger the move **/ void requestMove(Seat *seat, quint32 serial); /** * Requests a resize on the given @p seat after the pointer button press with the given @p serial. * * @param seat The seat on which to resize the window * @param serial The serial of the pointer button press which should trigger the resize * @param edges A hint for the compositor to set e.g. an appropriate cursor image **/ void requestResize(Seat *seat, quint32 serial, Qt::Edges edges); /** * When a configure event is received, if a client commits the * Surface in response to the configure event, then the client * must make an ackConfigure request sometime before the commit * request, passing along the @p serial of the configure event. * @see configureRequested **/ void ackConfigure(quint32 serial); /** * Request to set this XdgShellSurface to be maximized if @p set is @c true. * If @p set is @c false it requests to unset the maximized state - if set. * * @param set Whether the XdgShellSurface should be maximized **/ void setMaximized(bool set); /** * Request to set this XdgShellSurface as fullscreen on @p output. * If @p set is @c true the Surface should be set to fullscreen, otherwise restore * from fullscreen state. * * @param set Whether the Surface should be fullscreen or not * @param output Optional output as hint to the compositor where the Surface should be put **/ void setFullscreen(bool set, Output *output = nullptr); /** * Request to the compositor to minimize this XdgShellSurface. **/ void requestMinimize(); /* * Set this surface to have a given maximum size * @since 5.XDGMERGE_VERSION */ void setMaxSize(const QSize &size); /* * Set this surface to have a given minimum size * @since 5.XDGMERGE_VERSION */ void setMinSize(const QSize &size); operator xdg_surface*(); operator xdg_surface*() const; operator zxdg_surface_v6*(); operator zxdg_surface_v6*() const; operator zxdg_toplevel_v6*(); operator zxdg_toplevel_v6*() const; Q_SIGNALS: /** * The compositor requested to close this window. **/ void closeRequested(); /** * The compositor sent a configure with the new @p size and the @p states. * Before the next commit of the surface the @p serial needs to be passed to ackConfigure. **/ void configureRequested(const QSize &size, KWayland::Client::XdgShellSurface::States states, quint32 serial); /** * Emitted whenever the size of the XdgShellSurface changes by e.g. receiving a configure request. * * @see configureRequested * @see size * @see setSize **/ void sizeChanged(const QSize &); protected: class Private; explicit XdgShellSurface(Private *p, QObject *parent = nullptr); private: QScopedPointer d; }; /** * A XdgShellPopup is a short-lived, temporary surface that can be * used to implement menus. It takes an explicit grab on the surface * that will be dismissed when the user dismisses the popup. This can * be done by the user clicking outside the surface, using the keyboard, * or even locking the screen through closing the lid or a timeout. * @since 5.25 **/ class KWAYLANDCLIENT_EXPORT XdgShellPopup : public QObject { Q_OBJECT public: virtual ~XdgShellPopup(); /** * Setup this XdgShellPopup to manage the @p xdgpopupv5. * When using XdgShell::createXdgShellPopup there is no need to call this * method. **/ void setup(xdg_popup *xdgpopupv5); /** * Setup this XdgShellPopup to manage the @p xdgpopupv6 * When using XdgShell::createXdgShellPopup there is no need to call this * method. **/ void setup(zxdg_surface_v6 *xdgsurfacev6, zxdg_popup_v6 *xdgpopup6); /** * @returns @c true if managing an xdg_popup. **/ bool isValid() const; /** * Releases the xdg_popup interface. * After the interface has been released the XdgShellPopup instance is no * longer valid and can be setup with another xdg_popup interface. **/ void release(); /** * Destroys the data held by this XdgShellPopup. * 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 xdg_popup interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, xdgpopupv5, &XdgShellPopup::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for bound proxies. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for bound proxies. **/ EventQueue *eventQueue(); /** * Requests a grab on this popup * @since 5.XDGMERGE_VERSION */ void requestGrab(Seat *seat, quint32 serial); operator xdg_popup*(); operator xdg_popup*() const; operator zxdg_surface_v6*(); operator zxdg_surface_v6*() const; operator zxdg_popup_v6*(); operator zxdg_popup_v6*() const; Q_SIGNALS: /** * This signal is emitted when a XdgShellPopup is dismissed by the * compositor. The user should delete this instance at this point. **/ void popupDone(); /** * **/ void configureRequested(const QRect &relativePosition, quint32 serial); protected: class Private; explicit XdgShellPopup(Private *p, QObject *parent = nullptr); private: QScopedPointer d; }; } } Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Client::XdgShellSurface::States) Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Client::XdgPositioner::Constraints) Q_DECLARE_METATYPE(KWayland::Client::XdgPositioner) Q_DECLARE_METATYPE(KWayland::Client::XdgShellSurface::State) Q_DECLARE_METATYPE(KWayland::Client::XdgShellSurface::States) Q_DECLARE_METATYPE(KWayland::Client::XdgPositioner::Constraint) Q_DECLARE_METATYPE(KWayland::Client::XdgPositioner::Constraints) #endif diff --git a/src/server/keyboard_interface.cpp b/src/server/keyboard_interface.cpp index a5ffd8a..671fc1d 100644 --- a/src/server/keyboard_interface.cpp +++ b/src/server/keyboard_interface.cpp @@ -1,189 +1,192 @@ /******************************************************************** 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 "keyboard_interface.h" #include "keyboard_interface_p.h" #include "display.h" #include "seat_interface.h" #include "surface_interface.h" // Qt #include // Wayland #include namespace KWayland { namespace Server { KeyboardInterface::Private::Private(SeatInterface *s, wl_resource *parentResource, KeyboardInterface *q) : Resource::Private(q, s, parentResource, &wl_keyboard_interface, &s_interface) , seat(s) { } void KeyboardInterface::Private::focusChildSurface(const QPointer &childSurface, quint32 serial) { if (focusedChildSurface == childSurface) { return; } sendLeave(focusedChildSurface.data(), serial); focusedChildSurface = childSurface; sendEnter(focusedChildSurface.data(), serial); } void KeyboardInterface::Private::sendLeave(SurfaceInterface *surface, quint32 serial) { if (surface && resource && surface->resource()) { wl_keyboard_send_leave(resource, serial, surface->resource()); } } void KeyboardInterface::Private::sendEnter(SurfaceInterface *surface, quint32 serial) { wl_array keys; wl_array_init(&keys); const auto states = seat->pressedKeys(); for (auto it = states.begin(); it != states.end(); ++it) { uint32_t *k = reinterpret_cast(wl_array_add(&keys, sizeof(uint32_t))); *k = *it; } wl_keyboard_send_enter(resource, serial, surface->resource(), &keys); wl_array_release(&keys); sendModifiers(); } #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct wl_keyboard_interface KeyboardInterface::Private::s_interface { resourceDestroyedCallback }; #endif KeyboardInterface::KeyboardInterface(SeatInterface *parent, wl_resource *parentResource) : Resource(new Private(parent, parentResource, this)) { } KeyboardInterface::~KeyboardInterface() = default; void KeyboardInterface::setKeymap(int fd, quint32 size) { Q_D(); d->sendKeymap(fd, size); } void KeyboardInterface::Private::sendKeymap(int fd, quint32 size) { if (!resource) { return; } wl_keyboard_send_keymap(resource, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, fd, size); } void KeyboardInterface::Private::sendModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group, quint32 serial) { if (!resource) { return; } wl_keyboard_send_modifiers(resource, serial, depressed, latched, locked, group); } void KeyboardInterface::Private::sendModifiers() { sendModifiers(seat->depressedModifiers(), seat->latchedModifiers(), seat->lockedModifiers(), seat->groupModifiers(), seat->lastModifiersSerial()); } void KeyboardInterface::setFocusedSurface(SurfaceInterface *surface, quint32 serial) { Q_D(); d->sendLeave(d->focusedChildSurface, serial); disconnect(d->destroyConnection); d->focusedChildSurface.clear(); d->focusedSurface = surface; if (!d->focusedSurface) { return; } - d->destroyConnection = connect(d->focusedSurface, &QObject::destroyed, this, + d->destroyConnection = connect(d->focusedSurface, &Resource::aboutToBeUnbound, this, [this] { Q_D(); + if (d->resource) { + wl_keyboard_send_leave(d->resource, d->global->display()->nextSerial(), d->focusedSurface->resource()); + } d->focusedSurface = nullptr; d->focusedChildSurface.clear(); } ); d->focusedChildSurface = QPointer(surface); d->sendEnter(d->focusedSurface, serial); d->client->flush(); } void KeyboardInterface::keyPressed(quint32 key, quint32 serial) { Q_D(); if (!d->resource) { return; } Q_ASSERT(d->focusedSurface); wl_keyboard_send_key(d->resource, serial, d->seat->timestamp(), key, WL_KEYBOARD_KEY_STATE_PRESSED); } void KeyboardInterface::keyReleased(quint32 key, quint32 serial) { Q_D(); if (!d->resource) { return; } Q_ASSERT(d->focusedSurface); wl_keyboard_send_key(d->resource, serial, d->seat->timestamp(), key, WL_KEYBOARD_KEY_STATE_RELEASED); } void KeyboardInterface::updateModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group, quint32 serial) { Q_D(); Q_ASSERT(d->focusedSurface); d->sendModifiers(depressed, latched, locked, group, serial); } void KeyboardInterface::repeatInfo(qint32 charactersPerSecond, qint32 delay) { Q_D(); if (!d->resource) { return; } if (wl_resource_get_version(d->resource) < WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) { // only supported since version 4 return; } wl_keyboard_send_repeat_info(d->resource, charactersPerSecond, delay); } SurfaceInterface *KeyboardInterface::focusedSurface() const { Q_D(); return d->focusedSurface; } KeyboardInterface::Private *KeyboardInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/pointer_interface.cpp b/src/server/pointer_interface.cpp index 2900631..fc85995 100644 --- a/src/server/pointer_interface.cpp +++ b/src/server/pointer_interface.cpp @@ -1,417 +1,418 @@ /******************************************************************** 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 "pointer_interface.h" #include "pointer_interface_p.h" #include "pointerconstraints_interface.h" #include "pointergestures_interface_p.h" #include "resource_p.h" #include "relativepointer_interface_p.h" #include "seat_interface.h" #include "display.h" #include "subcompositor_interface.h" #include "surface_interface.h" // Wayland #include namespace KWayland { namespace Server { class Cursor::Private { public: Private(Cursor *q, PointerInterface *pointer); PointerInterface *pointer; quint32 enteredSerial = 0; QPoint hotspot; QPointer surface; void update(const QPointer &surface, quint32 serial, const QPoint &hotspot); private: Cursor *q; }; PointerInterface::Private::Private(SeatInterface *parent, wl_resource *parentResource, PointerInterface *q) : Resource::Private(q, parent, parentResource, &wl_pointer_interface, &s_interface) , seat(parent) { } void PointerInterface::Private::setCursor(quint32 serial, SurfaceInterface *surface, const QPoint &hotspot) { if (!cursor) { Q_Q(PointerInterface); cursor = new Cursor(q); cursor->d->update(QPointer(surface), serial, hotspot); QObject::connect(cursor, &Cursor::changed, q, &PointerInterface::cursorChanged); emit q->cursorChanged(); } else { cursor->d->update(QPointer(surface), serial, hotspot); } } void PointerInterface::Private::sendLeave(SurfaceInterface *surface, quint32 serial) { if (!surface) { return; } if (resource && surface->resource()) { wl_pointer_send_leave(resource, serial, surface->resource()); } } void PointerInterface::Private::registerRelativePointer(RelativePointerInterface *relativePointer) { relativePointers << relativePointer; QObject::connect(relativePointer, &QObject::destroyed, q, [this, relativePointer] { relativePointers.removeOne(relativePointer); } ); } void PointerInterface::Private::registerSwipeGesture(PointerSwipeGestureInterface *gesture) { swipeGestures << gesture; QObject::connect(gesture, &QObject::destroyed, q, [this, gesture] { swipeGestures.removeOne(gesture); } ); } void PointerInterface::Private::registerPinchGesture(PointerPinchGestureInterface *gesture) { pinchGestures << gesture; QObject::connect(gesture, &QObject::destroyed, q, [this, gesture] { pinchGestures.removeOne(gesture); } ); } namespace { static QPointF surfacePosition(SurfaceInterface *surface) { if (surface && surface->subSurface()) { return surface->subSurface()->position() + surfacePosition(surface->subSurface()->parentSurface().data()); } return QPointF(); } } void PointerInterface::Private::sendEnter(SurfaceInterface *surface, const QPointF &parentSurfacePosition, quint32 serial) { if (!surface || !surface->resource()) { return; } const QPointF adjustedPos = parentSurfacePosition - surfacePosition(surface); wl_pointer_send_enter(resource, serial, surface->resource(), wl_fixed_from_double(adjustedPos.x()), wl_fixed_from_double(adjustedPos.y())); } void PointerInterface::Private::startSwipeGesture(quint32 serial, quint32 fingerCount) { if (swipeGestures.isEmpty()) { return; } for (auto it = swipeGestures.constBegin(), end = swipeGestures.constEnd(); it != end; it++) { (*it)->start(serial, fingerCount); } } void PointerInterface::Private::updateSwipeGesture(const QSizeF &delta) { if (swipeGestures.isEmpty()) { return; } for (auto it = swipeGestures.constBegin(), end = swipeGestures.constEnd(); it != end; it++) { (*it)->update(delta); } } void PointerInterface::Private::endSwipeGesture(quint32 serial) { if (swipeGestures.isEmpty()) { return; } for (auto it = swipeGestures.constBegin(), end = swipeGestures.constEnd(); it != end; it++) { (*it)->end(serial); } } void PointerInterface::Private::cancelSwipeGesture(quint32 serial) { if (swipeGestures.isEmpty()) { return; } for (auto it = swipeGestures.constBegin(), end = swipeGestures.constEnd(); it != end; it++) { (*it)->cancel(serial); } } void PointerInterface::Private::startPinchGesture(quint32 serial, quint32 fingerCount) { if (pinchGestures.isEmpty()) { return; } for (auto it = pinchGestures.constBegin(), end = pinchGestures.constEnd(); it != end; it++) { (*it)->start(serial, fingerCount); } } void PointerInterface::Private::updatePinchGesture(const QSizeF &delta, qreal scale, qreal rotation) { if (pinchGestures.isEmpty()) { return; } for (auto it = pinchGestures.constBegin(), end = pinchGestures.constEnd(); it != end; it++) { (*it)->update(delta, scale, rotation); } } void PointerInterface::Private::endPinchGesture(quint32 serial) { if (pinchGestures.isEmpty()) { return; } for (auto it = pinchGestures.constBegin(), end = pinchGestures.constEnd(); it != end; it++) { (*it)->end(serial); } } void PointerInterface::Private::cancelPinchGesture(quint32 serial) { if (pinchGestures.isEmpty()) { return; } for (auto it = pinchGestures.constBegin(), end = pinchGestures.constEnd(); it != end; it++) { (*it)->cancel(serial); } } #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct wl_pointer_interface PointerInterface::Private::s_interface = { setCursorCallback, resourceDestroyedCallback }; #endif PointerInterface::PointerInterface(SeatInterface *parent, wl_resource *parentResource) : Resource(new Private(parent, parentResource, this)) { // TODO: handle touch connect(parent, &SeatInterface::pointerPosChanged, this, [this] { Q_D(); if (d->seat->isDragPointer()) { // handled by DataDevice return; } if (d->focusedSurface && d->resource) { if (!d->focusedSurface->lockedPointer().isNull() && d->focusedSurface->lockedPointer()->isLocked()) { return; } const QPointF pos = d->seat->focusedPointerSurfaceTransformation().map(d->seat->pointerPos()); auto targetSurface = d->focusedSurface->surfaceAt(pos); if (!targetSurface) { targetSurface = d->focusedSurface; } if (targetSurface != d->focusedChildSurface.data()) { const quint32 serial = d->seat->display()->nextSerial(); d->sendLeave(d->focusedChildSurface.data(), serial); d->focusedChildSurface = QPointer(targetSurface); d->sendEnter(targetSurface, pos, serial); d->client->flush(); } else { const QPointF adjustedPos = pos - surfacePosition(d->focusedChildSurface); wl_pointer_send_motion(d->resource, d->seat->timestamp(), wl_fixed_from_double(adjustedPos.x()), wl_fixed_from_double(adjustedPos.y())); } } }); } PointerInterface::~PointerInterface() = default; void PointerInterface::setFocusedSurface(SurfaceInterface *surface, quint32 serial) { Q_D(); d->sendLeave(d->focusedChildSurface.data(), serial); disconnect(d->destroyConnection); if (!surface) { d->focusedSurface = nullptr; d->focusedChildSurface.clear(); return; } d->focusedSurface = surface; - d->destroyConnection = connect(d->focusedSurface, &QObject::destroyed, this, + d->destroyConnection = connect(d->focusedSurface, &Resource::aboutToBeUnbound, this, [this] { Q_D(); + d->sendLeave(d->focusedChildSurface.data(), d->global->display()->nextSerial()); d->focusedSurface = nullptr; d->focusedChildSurface.clear(); } ); const QPointF pos = d->seat->focusedPointerSurfaceTransformation().map(d->seat->pointerPos()); d->focusedChildSurface = QPointer(d->focusedSurface->surfaceAt(pos)); if (!d->focusedChildSurface) { d->focusedChildSurface = QPointer(d->focusedSurface); } d->sendEnter(d->focusedChildSurface.data(), pos, serial); d->client->flush(); } void PointerInterface::buttonPressed(quint32 button, quint32 serial) { Q_D(); Q_ASSERT(d->focusedSurface); if (!d->resource) { return; } wl_pointer_send_button(d->resource, serial, d->seat->timestamp(), button, WL_POINTER_BUTTON_STATE_PRESSED); } void PointerInterface::buttonReleased(quint32 button, quint32 serial) { Q_D(); Q_ASSERT(d->focusedSurface); if (!d->resource) { return; } wl_pointer_send_button(d->resource, serial, d->seat->timestamp(), button, WL_POINTER_BUTTON_STATE_RELEASED); } void PointerInterface::axis(Qt::Orientation orientation, quint32 delta) { Q_D(); Q_ASSERT(d->focusedSurface); if (!d->resource) { return; } wl_pointer_send_axis(d->resource, d->seat->timestamp(), (orientation == Qt::Vertical) ? WL_POINTER_AXIS_VERTICAL_SCROLL : WL_POINTER_AXIS_HORIZONTAL_SCROLL, wl_fixed_from_int(delta)); } void PointerInterface::Private::setCursorCallback(wl_client *client, wl_resource *resource, uint32_t serial, wl_resource *surface, int32_t hotspot_x, int32_t hotspot_y) { auto p = cast(resource); Q_ASSERT(p->client->client() == client); p->setCursor(serial, SurfaceInterface::get(surface), QPoint(hotspot_x, hotspot_y)); } Cursor *PointerInterface::cursor() const { Q_D(); return d->cursor; } void PointerInterface::relativeMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds) { Q_D(); if (d->relativePointers.isEmpty()) { return; } for (auto it = d->relativePointers.constBegin(), end = d->relativePointers.constEnd(); it != end; it++) { (*it)->relativeMotion(delta, deltaNonAccelerated, microseconds); } client()->flush(); } PointerInterface::Private *PointerInterface::d_func() const { return reinterpret_cast(d.data()); } PointerInterface *PointerInterface::get(wl_resource *native) { return Private::get(native); } Cursor::Private::Private(Cursor *q, PointerInterface *pointer) : pointer(pointer) , q(q) { } void Cursor::Private::update(const QPointer< SurfaceInterface > &s, quint32 serial, const QPoint &p) { bool emitChanged = false; if (enteredSerial != serial) { enteredSerial = serial; emitChanged = true; emit q->enteredSerialChanged(); } if (hotspot != p) { hotspot = p; emitChanged = true; emit q->hotspotChanged(); } if (surface != s) { if (!surface.isNull()) { QObject::disconnect(surface.data(), &SurfaceInterface::damaged, q, &Cursor::changed); } surface = s; if (!surface.isNull()) { QObject::connect(surface.data(), &SurfaceInterface::damaged, q, &Cursor::changed); } emitChanged = true; emit q->surfaceChanged(); } if (emitChanged) { emit q->changed(); } } Cursor::Cursor(PointerInterface *parent) : QObject(parent) , d(new Private(this, parent)) { } Cursor::~Cursor() = default; quint32 Cursor::enteredSerial() const { return d->enteredSerial; } QPoint Cursor::hotspot() const { return d->hotspot; } PointerInterface *Cursor::pointer() const { return d->pointer; } QPointer< SurfaceInterface > Cursor::surface() const { return d->surface; } } } diff --git a/src/server/resource.cpp b/src/server/resource.cpp index eb735e9..f728ff3 100644 --- a/src/server/resource.cpp +++ b/src/server/resource.cpp @@ -1,120 +1,121 @@ /******************************************************************** 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 "resource.h" #include "resource_p.h" #include "clientconnection.h" #include namespace KWayland { namespace Server { QList Resource::Private::s_allResources; Resource::Private::Private(Resource *q, Global *g, wl_resource *parentResource, const wl_interface *interface, const void *implementation) : parentResource(parentResource) , global(g) , q(q) , m_interface(interface) , m_interfaceImplementation(implementation) { s_allResources << this; } Resource::Private::~Private() { s_allResources.removeAll(this); if (resource) { wl_resource_destroy(resource); } } void Resource::Private::create(ClientConnection *c, quint32 version, quint32 id) { Q_ASSERT(!resource); Q_ASSERT(!client); client = c; resource = client->createResource(m_interface, version, id); if (!resource) { return; } wl_resource_set_implementation(resource, m_interfaceImplementation, this, unbind); } void Resource::Private::unbind(wl_resource *r) { Private *p = cast(r); + emit p->q->aboutToBeUnbound(); p->resource = nullptr; emit p->q->unbound(); p->q->deleteLater(); } void Resource::Private::resourceDestroyedCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) wl_resource_destroy(resource); } Resource::Resource(Resource::Private *d, QObject *parent) : QObject(parent) , d(d) { } Resource::~Resource() = default; void Resource::create(ClientConnection *client, quint32 version, quint32 id) { d->create(client, version, id); } ClientConnection *Resource::client() { return d->client; } Global *Resource::global() { return d->global; } wl_resource *Resource::resource() { return d->resource; } wl_resource *Resource::parentResource() const { return d->parentResource; } quint32 Resource::id() const { if (!d->resource) { return 0; } return wl_resource_get_id(d->resource); } } } diff --git a/src/server/resource.h b/src/server/resource.h index 0cf51fb..c513815 100644 --- a/src/server/resource.h +++ b/src/server/resource.h @@ -1,94 +1,104 @@ /******************************************************************** 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_RESOURCE_H #define WAYLAND_SERVER_RESOURCE_H #include #include struct wl_client; struct wl_resource; namespace KWayland { namespace Server { class ClientConnection; class Global; /** * @brief Represents a bound Resource. * * A Resource normally gets created by a @link Global @endlink. * * The Resource is a base class for all specific resources and provides * access to various common aspects. **/ class KWAYLANDSERVER_EXPORT Resource : public QObject { Q_OBJECT public: virtual ~Resource(); void create(ClientConnection *client, quint32 version, quint32 id); /** * @returns the native wl_resource this Resource was created for. **/ wl_resource *resource(); /** * @returns The ClientConnection for which the Resource was created. **/ ClientConnection *client(); /** * @returns The Global which created the Resource. **/ Global *global(); /** * @returns the native parent wl_resource, e.g. the wl_resource bound on the Global **/ wl_resource *parentResource() const; /** * @returns The id of this Resource if it is created, otherwise @c 0. * * This is a convenient wrapper for wl_resource_get_id. * @since 5.3 **/ quint32 id() const; Q_SIGNALS: /** * This signal is emitted when the client unbound this Resource. * The Resource will be deleted in the next event cycle after this event. * @since 5.24 **/ void unbound(); + /** + * This signal is emitted when the client is in the process of unbinding the Resource. + * In opposite to @link{unbound} the @link{resource} is still valid and allows to perform + * cleanup tasks. Example: send a keyboard leave for the Surface which is in the process of + * getting destroyed. + * + * @see unbound + * @since 5.37 + **/ + void aboutToBeUnbound(); protected: class Private; explicit Resource(Private *d, QObject *parent = nullptr); QScopedPointer d; }; } } #endif diff --git a/src/server/seat_interface.cpp b/src/server/seat_interface.cpp index 3b17982..7a295f7 100644 --- a/src/server/seat_interface.cpp +++ b/src/server/seat_interface.cpp @@ -1,1493 +1,1493 @@ /******************************************************************** 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 (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, &QObject::destroyed, this, + 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/touch_interface.cpp b/src/server/touch_interface.cpp index df0332c..f49fa9d 100644 --- a/src/server/touch_interface.cpp +++ b/src/server/touch_interface.cpp @@ -1,127 +1,126 @@ /******************************************************************** 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 "touch_interface.h" #include "resource_p.h" #include "seat_interface.h" #include "display.h" #include "surface_interface.h" // Wayland #include namespace KWayland { namespace Server { class TouchInterface::Private : public Resource::Private { public: Private(SeatInterface *parent, wl_resource *parentResource, TouchInterface *q); SeatInterface *seat; - SurfaceInterface *focusedSurface = nullptr; QMetaObject::Connection destroyConnection; private: TouchInterface *q_func() { return reinterpret_cast(q); } static const struct wl_touch_interface s_interface; }; #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct wl_touch_interface TouchInterface::Private::s_interface = { resourceDestroyedCallback }; #endif TouchInterface::Private::Private(SeatInterface *parent, wl_resource *parentResource, TouchInterface *q) : Resource::Private(q, parent, parentResource, &wl_touch_interface, &s_interface) , seat(parent) { } TouchInterface::TouchInterface(SeatInterface *parent, wl_resource *parentResource) : Resource(new Private(parent, parentResource, this)) { } TouchInterface::~TouchInterface() = default; void TouchInterface::cancel() { Q_D(); if (!d->resource) { return; } wl_touch_send_cancel(d->resource); d->client->flush(); } void TouchInterface::frame() { Q_D(); if (!d->resource) { return; } wl_touch_send_frame(d->resource); d->client->flush(); } void TouchInterface::move(qint32 id, const QPointF &localPos) { Q_D(); if (!d->resource) { return; } wl_touch_send_motion(d->resource, d->seat->timestamp(), id, wl_fixed_from_double(localPos.x()), wl_fixed_from_double(localPos.y())); d->client->flush(); } void TouchInterface::up(qint32 id, quint32 serial) { Q_D(); if (!d->resource) { return; } wl_touch_send_up(d->resource, serial, d->seat->timestamp(), id); d->client->flush(); } void TouchInterface::down(qint32 id, quint32 serial, const QPointF &localPos) { Q_D(); if (!d->resource) { return; } wl_touch_send_down(d->resource, serial, d->seat->timestamp(), d->seat->focusedTouchSurface()->resource(), id, wl_fixed_from_double(localPos.x()), wl_fixed_from_double(localPos.y())); d->client->flush(); } TouchInterface::Private *TouchInterface::d_func() const { return reinterpret_cast(d.data()); } } }