diff options
Diffstat (limited to 'src/client')
122 files changed, 8030 insertions, 6035 deletions
diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt new file mode 100644 index 000000000..4d0a106f6 --- /dev/null +++ b/src/client/CMakeLists.txt @@ -0,0 +1,169 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +# Generated from client.pro. + +##################################################################### +## WaylandClient Module: +##################################################################### + +qt_internal_add_module(WaylandClient + PLUGIN_TYPES wayland-graphics-integration-client wayland-inputdevice-integration wayland-decoration-client wayland-shell-integration + SOURCES + ../shared/qwaylandinputmethodeventbuilder.cpp ../shared/qwaylandinputmethodeventbuilder_p.h + ../shared/qwaylandmimehelper.cpp ../shared/qwaylandmimehelper_p.h + ../shared/qwaylandsharedmemoryformathelper_p.h + global/qwaylandclientextension.cpp global/qwaylandclientextension.h global/qwaylandclientextension_p.h + hardwareintegration/qwaylandclientbufferintegration.cpp hardwareintegration/qwaylandclientbufferintegration_p.h + hardwareintegration/qwaylandclientbufferintegrationfactory.cpp hardwareintegration/qwaylandclientbufferintegrationfactory_p.h + hardwareintegration/qwaylandclientbufferintegrationplugin.cpp hardwareintegration/qwaylandclientbufferintegrationplugin_p.h + hardwareintegration/qwaylandhardwareintegration.cpp hardwareintegration/qwaylandhardwareintegration_p.h + hardwareintegration/qwaylandserverbufferintegration.cpp hardwareintegration/qwaylandserverbufferintegration_p.h + hardwareintegration/qwaylandserverbufferintegrationfactory.cpp hardwareintegration/qwaylandserverbufferintegrationfactory_p.h + hardwareintegration/qwaylandserverbufferintegrationplugin.cpp hardwareintegration/qwaylandserverbufferintegrationplugin_p.h + inputdeviceintegration/qwaylandinputdeviceintegration_p.h + inputdeviceintegration/qwaylandinputdeviceintegrationfactory.cpp inputdeviceintegration/qwaylandinputdeviceintegrationfactory_p.h + inputdeviceintegration/qwaylandinputdeviceintegrationplugin.cpp inputdeviceintegration/qwaylandinputdeviceintegrationplugin_p.h + qtwaylandclientglobal.h qtwaylandclientglobal_p.h + qwaylandabstractdecoration.cpp qwaylandabstractdecoration_p.h + qwaylandbuffer.cpp qwaylandbuffer_p.h + qwaylanddecorationfactory.cpp qwaylanddecorationfactory_p.h + qwaylanddecorationplugin.cpp qwaylanddecorationplugin_p.h + qwaylanddisplay.cpp qwaylanddisplay_p.h + qwaylandfractionalscale.cpp qwaylandfractionalscale_p.h + qwaylandinputcontext.cpp qwaylandinputcontext_p.h + qwaylandtextinputv1.cpp qwaylandtextinputv1_p.h + qwaylandtextinputv2.cpp qwaylandtextinputv2_p.h + qwaylandtextinputv3.cpp qwaylandtextinputv3_p.h + qwaylandtextinputinterface.cpp qwaylandtextinputinterface_p.h + qwaylandinputdevice.cpp qwaylandinputdevice_p.h + qwaylandinputmethodcontext.cpp qwaylandinputmethodcontext_p.h + qwaylandintegration.cpp qwaylandintegration_p.h + qwaylandnativeinterface.cpp qwaylandnativeinterface_p.h + qwaylandplatformservices.cpp qwaylandplatformservices_p.h + qwaylandpointergestures.cpp qwaylandpointergestures_p.h + qwaylandqtkey.cpp qwaylandqtkey_p.h + qwaylandscreen.cpp qwaylandscreen_p.h + qwaylandshellsurface.cpp qwaylandshellsurface_p.h + qwaylandshm.cpp qwaylandshm_p.h + qwaylandshmbackingstore.cpp qwaylandshmbackingstore_p.h + qwaylandshmwindow.cpp qwaylandshmwindow_p.h + qwaylandsubsurface.cpp qwaylandsubsurface_p.h + qwaylandsurface.cpp qwaylandsurface_p.h + qwaylandtouch.cpp qwaylandtouch_p.h + qwaylandviewport.cpp qwaylandviewport_p.h + qwaylandwindow.cpp qwaylandwindow_p.h + qwaylandwindowmanagerintegration.cpp qwaylandwindowmanagerintegration_p.h + shellintegration/qwaylandclientshellapi_p.h + shellintegration/qwaylandshellintegration_p.h shellintegration/qwaylandshellintegration.cpp + shellintegration/qwaylandshellintegrationfactory.cpp shellintegration/qwaylandshellintegrationfactory_p.h + shellintegration/qwaylandshellintegrationplugin.cpp shellintegration/qwaylandshellintegrationplugin_p.h + INCLUDE_DIRECTORIES + ../shared + global + hardwareintegration + inputdeviceintegration + shellintegration + LIBRARIES + Qt::CorePrivate + Qt::GuiPrivate + Qt::WaylandGlobalPrivate + PUBLIC_LIBRARIES + Qt::Core + Qt::Gui + Wayland::Client + PRIVATE_MODULE_INTERFACE + Qt::CorePrivate + Qt::GuiPrivate + Qt::WaylandGlobalPrivate + PRIVATE_HEADER_FILTERS + "^qwayland-.*\.h|^wayland-.*-protocol\.h" +) + +qt6_generate_wayland_protocol_client_sources(WaylandClient + FILES + ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/cursor-shape-v1.xml + ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/pointer-gestures-unstable-v1.xml + ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/tablet-unstable-v2.xml + ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/text-input-unstable-v1.xml + ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/text-input-unstable-v2.xml + ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/text-input-unstable-v3.xml + ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/wayland.xml + ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/wp-primary-selection-unstable-v1.xml + ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/xdg-output-unstable-v1.xml + ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/fractional-scale-v1.xml + ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/viewporter.xml + ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/xdg-shell.xml + ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/xdg-toplevel-drag-v1.xml + ${CMAKE_CURRENT_SOURCE_DIR}/../extensions/qt-key-unstable-v1.xml + ${CMAKE_CURRENT_SOURCE_DIR}/../extensions/qt-text-input-method-unstable-v1.xml + ${CMAKE_CURRENT_SOURCE_DIR}/../extensions/qt-windowmanager.xml + ${CMAKE_CURRENT_SOURCE_DIR}/../extensions/touch-extension.xml + ${CMAKE_CURRENT_SOURCE_DIR}/hardwareintegration/../../extensions/hardware-integration.xml + ${CMAKE_CURRENT_SOURCE_DIR}/hardwareintegration/../../extensions/server-buffer-extension.xml +) + +#### Keys ignored in scope 1:.:.:client.pro:<TRUE>: +# MODULE = "waylandclient" +# QMAKE_CXXFLAGS_WARN_ON = "--Wcast-qual" + +## Scopes: +##################################################################### + +# special case begin +# +# Do not explicitly add XKB::XKB. This is already done by Qt::GuiPrivate. +# +# qt_internal_extend_target(WaylandClient CONDITION QT_FEATURE_xkbcommon +# PUBLIC_LIBRARIES +# XKB::XKB +# ) +# special case end + +qt_internal_extend_target(WaylandClient CONDITION QT_FEATURE_tabletevent + SOURCES + qwaylandtabletv2.cpp qwaylandtabletv2_p.h +) + +qt_internal_extend_target(WaylandClient CONDITION QT_FEATURE_clipboard + SOURCES + qwaylandclipboard.cpp qwaylandclipboard_p.h +) + +qt_internal_extend_target(WaylandClient CONDITION QT_FEATURE_vulkan + SOURCES + qwaylandvulkaninstance.cpp qwaylandvulkaninstance_p.h + qwaylandvulkanwindow.cpp qwaylandvulkanwindow_p.h +) + +qt_internal_extend_target(WaylandClient CONDITION QT_FEATURE_cursor + SOURCES + qwaylandcursor.cpp qwaylandcursor_p.h + PUBLIC_LIBRARIES + Wayland::Cursor +) + +qt_internal_extend_target(WaylandClient CONDITION QT_FEATURE_wayland_datadevice + SOURCES + qwaylanddatadevice.cpp qwaylanddatadevice_p.h + qwaylanddatadevicemanager.cpp qwaylanddatadevicemanager_p.h + qwaylanddataoffer.cpp qwaylanddataoffer_p.h + qwaylanddatasource.cpp qwaylanddatasource_p.h +) + +qt_internal_extend_target(WaylandClient CONDITION QT_FEATURE_wayland_client_primary_selection + SOURCES + qwaylandprimaryselectionv1.cpp qwaylandprimaryselectionv1_p.h +) + +qt_internal_extend_target(WaylandClient CONDITION QT_FEATURE_draganddrop + SOURCES + qwaylanddnd.cpp qwaylanddnd_p.h +) + +qt_internal_add_docs(WaylandClient + doc/qtwaylandclient.qdocconf +) + +qt_record_extra_qt_main_tools_package_dependency(WaylandClient WaylandScannerTools "${PROJECT_VERSION}") +qt_record_extra_qt_package_dependency(WaylandClient WaylandGlobalPrivate "${PROJECT_VERSION}") diff --git a/src/client/Qt6WaylandClientMacros.cmake b/src/client/Qt6WaylandClientMacros.cmake new file mode 100644 index 000000000..b4266c558 --- /dev/null +++ b/src/client/Qt6WaylandClientMacros.cmake @@ -0,0 +1,119 @@ + +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +function(qt6_generate_wayland_protocol_client_sources target) + cmake_parse_arguments(arg + "NO_INCLUDE_CORE_ONLY;PRIVATE_CODE;PUBLIC_CODE" + "__QT_INTERNAL_WAYLAND_INCLUDE_DIR" + "FILES" + ${ARGN}) + + if(DEFINED arg_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown arguments were passed to qt6_generate_wayland_protocol_client_sources: (${arg_UNPARSED_ARGUMENTS}).") + endif() + + get_target_property(target_binary_dir ${target} BINARY_DIR) + + if(NOT TARGET Wayland::Scanner) + message(FATAL_ERROR "Wayland::Scanner target not found. You might be missing the WaylandScanner CMake package.") + endif() + + if(NOT TARGET Qt6::qtwaylandscanner) + message(FATAL_ERROR "qtwaylandscanner executable not found. Most likely there is an issue with your Qt installation.") + endif() + + string(TOUPPER "${target}" module_define_infix) + string(REPLACE "-" "_" module_define_infix "${module_define_infix}") + string(REPLACE "." "_" module_define_infix "${module_define_infix}") + set(build_macro "QT_BUILD_${module_define_infix}_LIB") + + foreach(protocol_file IN LISTS arg_FILES) + get_filename_component(protocol_name "${protocol_file}" NAME_WLE) + + set(waylandscanner_header_output "${target_binary_dir}/wayland-${protocol_name}-client-protocol.h") + set(waylandscanner_code_output "${target_binary_dir}/wayland-${protocol_name}-protocol.c") + # TODO: Maybe add "client" prefix or suffix to these in Qt6? + set(qtwaylandscanner_header_output "${target_binary_dir}/qwayland-${protocol_name}.h") + set(qtwaylandscanner_code_output "${target_binary_dir}/qwayland-${protocol_name}.cpp") + + if (NOT arg_NO_INCLUDE_CORE_ONLY) + set(waylandscanner_extra_args "--include-core-only") + endif() + + + if (arg_PRIVATE_CODE) + set(wayland_scanner_code_option "private-code") + else() + set(wayland_scanner_code_option "public-code") + endif() + + add_custom_command( + OUTPUT "${waylandscanner_header_output}" + #TODO: Maybe put the files in ${CMAKE_CURRENT_BINARY_DIR/wayland_generated instead? + COMMAND Wayland::Scanner ${waylandscanner_extra_args} client-header < "${protocol_file}" > "${waylandscanner_header_output}" + DEPENDS ${protocol_file} Wayland::Scanner + ) + + add_custom_command( + OUTPUT "${waylandscanner_code_output}" + COMMAND Wayland::Scanner ${waylandscanner_extra_args} ${wayland_scanner_code_option} < "${protocol_file}" > "${waylandscanner_code_output}" + DEPENDS ${protocol_file} Wayland::Scanner + ) + + set(wayland_include_dir "") + if(arg___QT_INTERNAL_WAYLAND_INCLUDE_DIR) + set(wayland_include_dir "${arg___QT_INTERNAL_WAYLAND_INCLUDE_DIR}") + else() + get_target_property(qt_module ${target} _qt_module_interface_name) + get_target_property(is_for_module "${target}" _qt_module_has_headers) + if (qt_module) + set(wayland_include_dir "Qt${qt_module}/private") + elseif (is_for_module) + set(wayland_include_dir "QtWaylandClient/private") + endif() + endif() + + add_custom_command( + OUTPUT "${qtwaylandscanner_header_output}" + COMMAND Qt6::qtwaylandscanner client-header + "${protocol_file}" + --build-macro=${build_macro} + --header-path="${wayland_include_dir}" + > "${qtwaylandscanner_header_output}" + DEPENDS ${protocol_file} Qt6::qtwaylandscanner + ) + + set(qtwaylandscanner_code_include "") + if (is_for_module) + set(qtwaylandscanner_code_include "<QtWaylandClient/private/wayland-wayland-client-protocol.h>") + endif() + + add_custom_command( + OUTPUT "${qtwaylandscanner_code_output}" + COMMAND Qt6::qtwaylandscanner client-code + "${protocol_file}" + --build-macro=${build_macro} + --header-path='${wayland_include_dir}' + --add-include='${qtwaylandscanner_code_include}' + > "${qtwaylandscanner_code_output}" + DEPENDS ${protocol_file} Qt6::qtwaylandscanner + ) + + set(sources "${waylandscanner_header_output}" + "${waylandscanner_code_output}" + "${qtwaylandscanner_header_output}" + "${qtwaylandscanner_code_output}") + + target_sources(${target} PRIVATE ${sources}) + + set_source_files_properties(${sources} PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON) + endforeach() + target_include_directories(${target} PRIVATE ${target_binary_dir}) +endfunction() + +if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) + function(qt_generate_wayland_protocol_client_sources) + qt6_generate_wayland_protocol_client_sources(${ARGV}) + endfunction() +endif() diff --git a/src/client/client.pro b/src/client/client.pro deleted file mode 100644 index 458e49360..000000000 --- a/src/client/client.pro +++ /dev/null @@ -1,154 +0,0 @@ -TARGET = QtWaylandClient -MODULE = waylandclient - -QT += core-private gui-private -QT_FOR_PRIVATE += service_support-private -QT_PRIVATE += fontdatabase_support-private eventdispatcher_support-private theme_support-private - -qtConfig(vulkan) { - QT_PRIVATE += vulkan_support-private -} - -# We have a bunch of C code with casts, so we can't have this option -QMAKE_CXXFLAGS_WARN_ON -= -Wcast-qual - -# Prevent gold linker from crashing. -# This started happening when QtPlatformSupport was modularized. -use_gold_linker: CONFIG += no_linker_version_script - -CONFIG -= precompile_header -CONFIG += link_pkgconfig wayland-scanner - -qtConfig(xkbcommon) { - QT_FOR_PRIVATE += xkbcommon_support-private -} - -qtHaveModule(linuxaccessibility_support_private): \ - QT_PRIVATE += linuxaccessibility_support_private - -QMAKE_USE += wayland-client - -INCLUDEPATH += $$PWD/../shared - -WAYLANDCLIENTSOURCES += \ - ../extensions/surface-extension.xml \ - ../extensions/touch-extension.xml \ - ../extensions/qt-key-unstable-v1.xml \ - ../extensions/qt-windowmanager.xml \ - ../3rdparty/protocol/wp-primary-selection-unstable-v1.xml \ - ../3rdparty/protocol/text-input-unstable-v2.xml \ - ../3rdparty/protocol/xdg-output-unstable-v1.xml \ - ../3rdparty/protocol/wayland.xml - -SOURCES += qwaylandintegration.cpp \ - qwaylandnativeinterface.cpp \ - qwaylandshmbackingstore.cpp \ - qwaylandinputdevice.cpp \ - qwaylanddisplay.cpp \ - qwaylandwindow.cpp \ - qwaylandscreen.cpp \ - qwaylandshmwindow.cpp \ - qwaylandshellsurface.cpp \ - qwaylandextendedsurface.cpp \ - qwaylandsubsurface.cpp \ - qwaylandsurface.cpp \ - qwaylandtouch.cpp \ - qwaylandqtkey.cpp \ - ../shared/qwaylandmimehelper.cpp \ - ../shared/qwaylandinputmethodeventbuilder.cpp \ - qwaylandabstractdecoration.cpp \ - qwaylanddecorationfactory.cpp \ - qwaylanddecorationplugin.cpp \ - qwaylandwindowmanagerintegration.cpp \ - qwaylandinputcontext.cpp \ - qwaylandshm.cpp \ - qwaylandbuffer.cpp \ - -HEADERS += qwaylandintegration_p.h \ - qwaylandnativeinterface_p.h \ - qwaylanddisplay_p.h \ - qwaylandwindow_p.h \ - qwaylandscreen_p.h \ - qwaylandshmbackingstore_p.h \ - qwaylandinputdevice_p.h \ - qwaylandbuffer_p.h \ - qwaylandshmwindow_p.h \ - qwaylandshellsurface_p.h \ - qwaylandextendedsurface_p.h \ - qwaylandsubsurface_p.h \ - qwaylandsurface_p.h \ - qwaylandtouch_p.h \ - qwaylandqtkey_p.h \ - qwaylandabstractdecoration_p.h \ - qwaylanddecorationfactory_p.h \ - qwaylanddecorationplugin_p.h \ - qwaylandwindowmanagerintegration_p.h \ - qwaylandinputcontext_p.h \ - qwaylandshm_p.h \ - qtwaylandclientglobal.h \ - qtwaylandclientglobal_p.h \ - ../shared/qwaylandinputmethodeventbuilder_p.h \ - ../shared/qwaylandmimehelper_p.h \ - ../shared/qwaylandsharedmemoryformathelper_p.h \ - -qtConfig(clipboard) { - HEADERS += qwaylandclipboard_p.h - SOURCES += qwaylandclipboard.cpp -} - -include(hardwareintegration/hardwareintegration.pri) -include(shellintegration/shellintegration.pri) -include(inputdeviceintegration/inputdeviceintegration.pri) -include(global/global.pri) - -qtConfig(vulkan) { - HEADERS += \ - qwaylandvulkaninstance_p.h \ - qwaylandvulkanwindow_p.h - - SOURCES += \ - qwaylandvulkaninstance.cpp \ - qwaylandvulkanwindow.cpp -} - -qtConfig(cursor) { - QMAKE_USE += wayland-cursor - - HEADERS += \ - qwaylandcursor_p.h - SOURCES += \ - qwaylandcursor.cpp -} - -qtConfig(wayland-datadevice) { - HEADERS += \ - qwaylanddatadevice_p.h \ - qwaylanddatadevicemanager_p.h \ - qwaylanddataoffer_p.h \ - qwaylanddatasource_p.h - SOURCES += \ - qwaylanddatadevice.cpp \ - qwaylanddatadevicemanager.cpp \ - qwaylanddataoffer.cpp \ - qwaylanddatasource.cpp -} - -qtConfig(wayland-client-primary-selection) { - HEADERS += qwaylandprimaryselectionv1_p.h - SOURCES += qwaylandprimaryselectionv1.cpp -} - -qtConfig(draganddrop) { - HEADERS += \ - qwaylanddnd_p.h - SOURCES += \ - qwaylanddnd.cpp -} - -CONFIG += generated_privates -MODULE_PLUGIN_TYPES = \ - wayland-graphics-integration-client \ - wayland-inputdevice-integration \ - wayland-decoration-client \ - wayland-shell-integration -load(qt_module) diff --git a/src/client/configure.cmake b/src/client/configure.cmake new file mode 100644 index 000000000..5ed7995ef --- /dev/null +++ b/src/client/configure.cmake @@ -0,0 +1,56 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +#### Inputs + + + +#### Libraries + + +# Even if libdrm is already found by qtbase we still need to list it as dependency for some of our +# plugins +if(TARGET Libdrm::Libdrm) + qt_internal_disable_find_package_global_promotion(Libdrm::Libdrm) +endif() +qt_find_package(Libdrm PROVIDED_TARGETS Libdrm::Libdrm MODULE_NAME gui QMAKE_LIB drm MARK_OPTIONAL) + +#### Tests + + +#### Features + +qt_feature("wayland-client-primary-selection" PRIVATE + LABEL "primary-selection clipboard" + CONDITION QT_FEATURE_clipboard +) +qt_feature("wayland-client-fullscreen-shell-v1" PRIVATE + LABEL "fullscreen-shell-v1" + CONDITION QT_FEATURE_wayland_client +) +qt_feature("wayland-client-ivi-shell" PRIVATE + LABEL "ivi-shell" + CONDITION QT_FEATURE_wayland_client +) +qt_feature("wayland-client-wl-shell" PRIVATE + LABEL "wl-shell (deprecated)" + CONDITION QT_FEATURE_wayland_client +) +qt_feature("wayland-client-xdg-shell" PRIVATE + LABEL "xdg-shell" + CONDITION QT_FEATURE_wayland_client +) +qt_feature("wayland-client-qt-shell" PRIVATE + LABEL "qt-shell" + CONDITION QT_FEATURE_wayland_client +) +qt_feature("egl-extension-platform-wayland" PRIVATE + LABEL "EGL wayland platform extension" + CONDITION QT_FEATURE_wayland_client AND QT_FEATURE_opengl AND QT_FEATURE_egl AND TEST_egl_1_5_wayland +) +qt_configure_add_summary_section(NAME "Qt Wayland Client Shell Integrations") +qt_configure_add_summary_entry(ARGS "wayland-client-xdg-shell") +qt_configure_add_summary_entry(ARGS "wayland-client-ivi-shell") +qt_configure_add_summary_entry(ARGS "wayland-client-wl-shell") +qt_configure_add_summary_entry(ARGS "wayland-client-qt-shell") +qt_configure_end_summary_section() # end of "Qt Wayland Client Shell Integrations" section diff --git a/src/client/configure.json b/src/client/configure.json deleted file mode 100644 index f49beaf70..000000000 --- a/src/client/configure.json +++ /dev/null @@ -1,300 +0,0 @@ -{ - "module": "waylandclient", - "condition": "module.gui", - "depends": [ - "gui-private" - ], - "testDir": "../../config.tests", - - "libraries": { - "wayland-client": { - "label": "Wayland client library", - "headers": "wayland-version.h", - "test": { - "main": [ - "#if WAYLAND_VERSION_MAJOR < 1", - "# error Wayland 1.8.0 or higher required", - "#endif", - "#if WAYLAND_VERSION_MAJOR == 1", - "# if WAYLAND_VERSION_MINOR < 8", - "# error Wayland 1.8.0 or higher required", - "# endif", - "# if WAYLAND_VERSION_MINOR == 8", - "# if WAYLAND_VERSION_MICRO < 0", - "# error Wayland 1.8.0 or higher required", - "# endif", - "# endif", - "#endif" - ] - }, - "sources": [ - { "type": "pkgConfig", "args": "wayland-client" }, - "-lwayland-client" - ] - }, - "wayland-cursor": { - "label": "Wayland cursor library", - "headers": "wayland-cursor.h", - "test": { - "main": "struct wl_cursor_image *image = 0;" - }, - "use": "wayland-client", - "sources": [ - { "type": "pkgConfig", "args": "wayland-cursor" }, - "-lwayland-cursor" - ] - }, - "wayland-egl": { - "label": "Wayland EGL library", - "headers": "wayland-egl.h", - "test": { - "main": "struct wl_egl_window *window = wl_egl_window_create(0, 100, 100);" - }, - "sources": [ - { "type": "pkgConfig", "args": "wayland-egl" }, - "-lwayland-egl", - "-lEGL" - ] - }, - "xcomposite": { - "label": "XComposite", - "headers": "X11/extensions/Xcomposite.h", - "test": { - "main": "XCompositeRedirectWindow((Display *)0,(Window) 0, CompositeRedirectManual);" - - }, - "sources": [ - { "type": "pkgConfig", "args": "xcomposite" }, - "-lxcomposite" - ] - }, - "glx": { - "label": "GLX", - "headers": "GL/glx.h", - "test": { - "main": [ - "Display *dpy = XOpenDisplay(0);", - "int items = 0;", - "GLXFBConfig *fbc = glXChooseFBConfig(dpy, DefaultScreen(dpy), 0 , &items);" - ] - }, - "sources": [ - { "type": "pkgConfig", "args": "x11 gl" }, - "-lX11 -lGl" - ] - } - }, - - "tests": { - "wayland-scanner": { - "label": "wayland-scanner", - "type": "compile", - "test": "wayland_scanner", - "use": "wayland-client" - }, - "drm-egl-server": { - "label": "DRM EGL Server", - "type": "compile", - "test": { - "include": [ - "EGL/egl.h", - "EGL/eglext.h" - ], - "main": [ - "#ifdef EGL_MESA_drm_image", - "return 0;", - "#else", - "#error Requires EGL_MESA_drm_image to be defined", - "return 1;", - "#endif" - ] - }, - "use": "egl" - }, - "libhybris-egl-server": { - "label": "libhybris EGL Server", - "type": "compile", - "test": { - "include": [ - "EGL/egl.h", - "EGL/eglext.h", - "hybris/eglplatformcommon/hybris_nativebufferext.h" - ], - "main": [ - "#ifdef EGL_HYBRIS_native_buffer", - "return 0;", - "#else", - "#error Requires EGL_HYBRIS_native_buffer to be defined", - "return 1;", - "#endif" - ] - }, - "use": "egl" - }, - "dmabuf-server-buffer": { - "label": "Linux dma-buf Buffer Sharing", - "type": "compile", - "test": { - "include": [ - "EGL/egl.h", - "EGL/eglext.h", - "drm_fourcc.h" - ], - "main": [ - "#ifdef EGL_LINUX_DMA_BUF_EXT", - "return 0;", - "#else", - "#error Requires EGL_LINUX_DMA_BUF_EXT", - "return 1;", - "#endif" - ] - }, - "use": "egl" - }, - "vulkan-server-buffer": { - "label": "Vulkan Buffer Sharing", - "type": "compile", - "test": { - "include": [ - "vulkan/vulkan.h" - ], - "main": [ - "VkExportMemoryAllocateInfoKHR exportAllocInfo = {};", - "exportAllocInfo.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR;", - "exportAllocInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;", - "return 0;" - ] - } - } - }, - - "features": { - "wayland-client": { - "label": "Qt Wayland Client", - "condition": "!config.win32 && libs.wayland-client && libs.wayland-cursor && tests.wayland-scanner", - "output": [ "privateFeature" ] - }, - "wayland-datadevice": { - "condition": "features.draganddrop || features.clipboard", - "output": [ "privateFeature" ] - }, - "wayland-client-primary-selection": { - "label": "primary-selection clipboard", - "condition": "features.clipboard", - "output": [ "privateFeature" ] - }, - "wayland-client-fullscreen-shell-v1": { - "label": "fullscreen-shell-v1", - "condition": "features.wayland-client", - "output": [ "privateFeature" ] - }, - "wayland-client-ivi-shell": { - "label": "ivi-shell", - "condition": "features.wayland-client", - "output": [ "privateFeature" ] - }, - "wayland-client-wl-shell": { - "label": "wl-shell (deprecated)", - "condition": "features.wayland-client", - "output": [ "privateFeature" ] - }, - "wayland-client-xdg-shell": { - "label": "xdg-shell", - "condition": "features.wayland-client", - "output": [ "privateFeature" ] - }, - "wayland-client-xdg-shell-v5": { - "label": "xdg-shell unstable v5 (deprecated)", - "condition": "features.wayland-client", - "output": [ "privateFeature" ] - }, - "wayland-client-xdg-shell-v6": { - "label": "xdg-shell unstable v6", - "condition": "features.wayland-client", - "output": [ "privateFeature" ] - }, - "wayland-egl": { - "label": "EGL", - "condition": "features.wayland-client && features.opengl && features.egl && libs.wayland-egl", - "output": [ "privateFeature" ] - }, - "wayland-brcm": { - "label": "Raspberry Pi", - "condition": "features.wayland-client && features.eglfs_brcm", - "output": [ "privateFeature" ] - }, - "xcomposite-egl": { - "label": "XComposite EGL", - "condition": "features.wayland-client && features.opengl && features.egl && features.xlib && libs.xcomposite && features.egl_x11", - "output": [ "privateFeature" ] - }, - "xcomposite-glx": { - "label": "XComposite GLX", - "condition": "features.wayland-client && features.opengl && !features.opengles2 && features.xlib && libs.xcomposite && libs.glx", - "output": [ "privateFeature" ] - }, - "wayland-drm-egl-server-buffer": { - "label": "DRM EGL", - "condition": "features.wayland-client && features.opengl && features.egl && tests.drm-egl-server", - "output": [ "privateFeature" ] - }, - "wayland-libhybris-egl-server-buffer": { - "label": "libhybris EGL", - "condition": "features.wayland-client && features.opengl && features.egl && tests.libhybris-egl-server", - "output": [ "privateFeature" ] - }, - "wayland-dmabuf-server-buffer": { - "label": "Linux dma-buf server buffer integration", - "condition": "features.wayland-client && features.opengl && features.egl && tests.dmabuf-server-buffer", - "output": [ "privateFeature" ] - }, - "wayland-vulkan-server-buffer": { - "label": "Vulkan-based server buffer integration", - "condition": "features.wayland-client && features.opengl && features.egl && tests.vulkan-server-buffer", - "output": [ "privateFeature" ] - }, - "wayland-shm-emulation-server-buffer": { - "label": "Shm emulation server buffer integration", - "condition": "features.wayland-client && features.opengl", - "output": [ "privateFeature" ] - } - }, - - "report": [ - { - "type": "note", - "condition": "!libs.wayland-egl", - "message": "No wayland-egl support detected. Cross-toolkit compatibility disabled." - } - ], - - "summary": [ - { - "section": "Qt Wayland Drivers", - "condition": "features.wayland-client", - "entries": [ - "wayland-egl", - "wayland-brcm", - "xcomposite-egl", - "xcomposite-glx", - "wayland-drm-egl-server-buffer", - "wayland-libhybris-egl-server-buffer", - "wayland-dmabuf-server-buffer", - "wayland-vulkan-server-buffer", - "wayland-shm-emulation-server-buffer" - ] - }, - { - "section": "Qt Wayland Client Shell Integrations", - "condition": "features.wayland-client", - "entries": [ - "wayland-client-xdg-shell", - "wayland-client-xdg-shell-v5", - "wayland-client-xdg-shell-v6", - "wayland-client-ivi-shell", - "wayland-client-wl-shell" - ] - }, - "wayland-client" - ] -} diff --git a/src/client/doc/qtwaylandclient.qdocconf b/src/client/doc/qtwaylandclient.qdocconf new file mode 100644 index 000000000..20ceaef02 --- /dev/null +++ b/src/client/doc/qtwaylandclient.qdocconf @@ -0,0 +1,31 @@ +include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) +include($QT_INSTALL_DOCS/config/exampleurl-qtwayland.qdocconf) + +project = QtWaylandClient +description = Qt Wayland Client Reference Documentation +version = $QT_VERSION + +qhp.projects = QtWaylandClient + +qhp.QtWaylandClient.file = qtwaylandclient.qhp +qhp.QtWaylandClient.namespace = org.qt-project.QtWaylandClient.$QT_VERSION_TAG +qhp.QtWaylandClient.virtualFolder = QtWaylandClient +qhp.QtWaylandClient.indexTitle = Qt Wayland Client +qhp.QtWaylandClient.indexRoot = + +depends += qtcore \ + qtqml \ + qtquick \ + qtdoc \ + qtcmake \ + qtwaylandcompositor + +headerdirs += \ + ../ +sourcedirs += \ + ../ + +navigation.landingpage = "Qt Wayland Client" + +# Enforce zero documentation warnings +warninglimit = 0 diff --git a/src/client/doc/src/cmake/qt_generate_wayland_protocol_client_sources.qdoc b/src/client/doc/src/cmake/qt_generate_wayland_protocol_client_sources.qdoc new file mode 100644 index 000000000..0abd14d4e --- /dev/null +++ b/src/client/doc/src/cmake/qt_generate_wayland_protocol_client_sources.qdoc @@ -0,0 +1,51 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! +\page qt-generate-wayland-protocol-client-sources.html +\ingroup cmake-commands-qtwaylandclient + +\title qt_generate_wayland_protocol_client_sources +\keyword qt6_generate_wayland_protocol_client_sources + +\summary {Generates client-side C++ bindings for a Wayland protocol .XML file} + +\cmakecommandsince 6.0 + +The command is defined in the \c WaylandClient component of the \c Qt6 package, which +can be loaded like so: + +\badcode +find_package(Qt6 REQUIRED COMPONENTS WaylandClient) +\endcode + +\section1 Synopsis + +\badcode +qt_generate_wayland_protocol_client_sources(target + [PUBLIC_CODE | PRIVATE_CODE] + FILES file1.xml [file2.xml ...]) +\endcode + +\versionlessCMakeCommandsNote qt6_generate_wayland_protocol_client_sources() + +\section1 Description + +qt_generate_wayland_protocol_client_sources() creates the build steps to run \c{wayland-scanner} and +\c{qtwaylandscanner} on one or more Wayland protocol files. The tools will in turn generate binding +code in C and C++ for implementing the protocols, and the resulting files will be built as part +of the \c target. + +The options \c{PUBLIC_CODE} and \c{PRIVATE_CODE} correspond to the \c{public-code} and +\c{private-code} options of \c{wayland-scanner}. \c{PUBLIC_CODE} will cause the symbols in the +code that is generated by \c{wayland-scanner} to be exported. For backwards compatibility \c{PUBLIC_CODE} is the +default but generally \c{PRIVATE_CODE} is strongly recommended. + +qt_generate_wayland_protocol_client_sources() will trigger generation of the files needed to +implement the client side of the protocol. \l{qt_generate_wayland_protocol_server_sources}{qt_generate_wayland_protocol_server_sources()} +is the equivalent function for the compositor. + +See the \l{Custom Shell} or \l{Custom Extension} examples for a demonstration of how to use these +functions. +*/ + diff --git a/src/client/doc/src/qtwaylandclient-overview.qdoc b/src/client/doc/src/qtwaylandclient-overview.qdoc new file mode 100644 index 000000000..a6f5ce56a --- /dev/null +++ b/src/client/doc/src/qtwaylandclient-overview.qdoc @@ -0,0 +1,36 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page qtwaylandclient-index.html + \title Qt Wayland Client + \brief Library to enable connecting to a Wayland compositor as a client + + The Qt Wayland Client library provides the necessary functions for an application to act + as a \l {https://wayland.freedesktop.org/}{Wayland} client and connect to a Wayland compositor. + For most use cases, the library is used automatically through the Wayland QPA plugin, and there is + no need for the application itself to use any functions from the library. + + However, when paired with \l{Qt Wayland Compositor}, the cmake function + \l{qt_generate_wayland_protocol_client_sources}{qt_generate_wayland_protocol_client_sources()} + can be used to create custom protocol extensions. + + \section1 Licenses and Attributions + + Qt Wayland Compositor and the Qt Wayland integration plugin + are available under commercial licenses from \l{The Qt Company}. + + In addition, Qt Wayland Compositor is available under the + \l{GNU General Public License, version 3}, while + the Qt Wayland integration plugin is available under the + \l{GNU Lesser General Public License, version 3} or the + \l{GNU General Public License, version 2}. + + See \l{Qt Licensing} for further details. + + Qt Wayland Compositor and the Qt Wayland integration plugin + use protocol definitions under following permissive licenses: + + \generatelist{groupsbymodule attributions-qtwaylandcompositor} + +*/ diff --git a/src/client/global/qwaylandclientextension.cpp b/src/client/global/qwaylandclientextension.cpp index 125b1e19d..b2783088b 100644 --- a/src/client/global/qwaylandclientextension.cpp +++ b/src/client/global/qwaylandclientextension.cpp @@ -1,80 +1,61 @@ -/**************************************************************************** -** -** Copyright (C) 2017 Erik Larsson. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWaylandCompositor module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 Erik Larsson. +// Copyright (C) 2021 David Redondo <qt@david-redondo.de> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandclientextension.h" #include "qwaylandclientextension_p.h" #include <QtWaylandClient/private/qwaylanddisplay_p.h> #include <QtWaylandClient/private/qwaylandintegration_p.h> -#include <QtGui/QGuiApplication> -#include <QtGui/qpa/qplatformnativeinterface.h> -#include <QtGui/private/qguiapplication_p.h> -#include <QtCore/QDebug> QT_BEGIN_NAMESPACE +using RegistryGlobal = QtWaylandClient::QWaylandDisplay::RegistryGlobal; + QWaylandClientExtensionPrivate::QWaylandClientExtensionPrivate() { // Keep the possibility to use a custom waylandIntegration as a plugin, // but also add the possibility to run it as a QML component. - waylandIntegration = static_cast<QtWaylandClient::QWaylandIntegration *>(QGuiApplicationPrivate::platformIntegration()); + waylandIntegration = QtWaylandClient::QWaylandIntegration::instance(); if (!waylandIntegration) waylandIntegration = new QtWaylandClient::QWaylandIntegration(); +} - if (!waylandIntegration->nativeInterface()->nativeResourceForIntegration("wl_display")) - qWarning() << "This application requires a Wayland platform plugin"; +void QWaylandClientExtensionPrivate::globalAdded(const RegistryGlobal &global) +{ + Q_Q(QWaylandClientExtension); + if (!active && global.interface == QLatin1String(q->extensionInterface()->name)) { + q->bind(global.registry, global.id, global.version); + active = true; + emit q->activeChanged(); + } } -void QWaylandClientExtensionPrivate::handleRegistryGlobal(void *data, ::wl_registry *registry, uint32_t id, - const QString &interface, uint32_t version) +void QWaylandClientExtensionPrivate::globalRemoved(const RegistryGlobal &global) { - QWaylandClientExtension *extension = static_cast<QWaylandClientExtension *>(data); - if (interface == QLatin1String(extension->extensionInterface()->name) && !extension->d_func()->active) { - extension->bind(registry, id, version); - extension->d_func()->active = true; - emit extension->activeChanged(); + Q_Q(QWaylandClientExtension); + if (active && global.interface == QLatin1String(q->extensionInterface()->name)) { + active = false; + emit q->activeChanged(); } } -void QWaylandClientExtension::addRegistryListener() +void QWaylandClientExtension::initialize() { Q_D(QWaylandClientExtension); - d->waylandIntegration->display()->addRegistryListener(&QWaylandClientExtensionPrivate::handleRegistryGlobal, this); + if (d->active) { + return; + } + const QtWaylandClient::QWaylandDisplay *display = d->waylandIntegration->display(); + const auto globals = display->globals(); + auto global = + std::find_if(globals.cbegin(), globals.cend(), [this](const RegistryGlobal &global) { + return global.interface == QLatin1String(extensionInterface()->name); + }); + if (global != globals.cend()) { + bind(global->registry, global->id, global->version); + d->active = true; + emit activeChanged(); + } } QWaylandClientExtension::QWaylandClientExtension(const int ver) @@ -82,10 +63,17 @@ QWaylandClientExtension::QWaylandClientExtension(const int ver) { Q_D(QWaylandClientExtension); d->version = ver; + auto display = d->waylandIntegration->display(); + QObjectPrivate::connect(display, &QtWaylandClient::QWaylandDisplay::globalAdded, d, + &QWaylandClientExtensionPrivate::globalAdded); + QObjectPrivate::connect(display, &QtWaylandClient::QWaylandDisplay::globalRemoved, d, + &QWaylandClientExtensionPrivate::globalRemoved); + // This function uses virtual functions and we don't want it to be called from the constructor. + QMetaObject::invokeMethod(this, "initialize", Qt::QueuedConnection); +} - // The registry listener uses virtual functions and we don't want it to be called from - // the constructor. - QMetaObject::invokeMethod(this, "addRegistryListener", Qt::QueuedConnection); +QWaylandClientExtension::~QWaylandClientExtension() +{ } QtWaylandClient::QWaylandIntegration *QWaylandClientExtension::integration() const @@ -116,3 +104,5 @@ bool QWaylandClientExtension::isActive() const } QT_END_NAMESPACE + +#include "moc_qwaylandclientextension.cpp" diff --git a/src/client/global/qwaylandclientextension.h b/src/client/global/qwaylandclientextension.h index 98272e571..c57549c34 100644 --- a/src/client/global/qwaylandclientextension.h +++ b/src/client/global/qwaylandclientextension.h @@ -1,46 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2017 Erik Larsson. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWaylandCompositor module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 Erik Larsson. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDCLIENTEXTENSION_H #define QWAYLANDCLIENTEXTENSION_H -#include <QObject> +#include <QtCore/QObject> #include <QtWaylandClient/qtwaylandclientglobal.h> struct wl_interface; @@ -55,7 +19,7 @@ class QWaylandIntegration; class QWaylandClientExtensionPrivate; class QWaylandClientExtensionTemplatePrivate; -class Q_WAYLAND_CLIENT_EXPORT QWaylandClientExtension : public QObject +class Q_WAYLANDCLIENT_EXPORT QWaylandClientExtension : public QObject { Q_OBJECT Q_DECLARE_PRIVATE(QWaylandClientExtension) @@ -63,6 +27,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandClientExtension : public QObject Q_PROPERTY(bool active READ isActive NOTIFY activeChanged) public: QWaylandClientExtension(const int version); + ~QWaylandClientExtension(); QtWaylandClient::QWaylandIntegration *integration() const; int version() const; @@ -76,18 +41,35 @@ Q_SIGNALS: void versionChanged(); void activeChanged(); -private Q_SLOTS: - void addRegistryListener(); +protected Q_SLOTS: + void initialize(); }; -template <typename T> -class Q_WAYLAND_CLIENT_EXPORT QWaylandClientExtensionTemplate : public QWaylandClientExtension + +template<typename T, auto destruct = nullptr> +class Q_WAYLANDCLIENT_EXPORT QWaylandClientExtensionTemplate : public QWaylandClientExtension { Q_DECLARE_PRIVATE(QWaylandClientExtensionTemplate) + public: - QWaylandClientExtensionTemplate(const int ver) : - QWaylandClientExtension(ver) + QWaylandClientExtensionTemplate(const int ver) : QWaylandClientExtension(ver) { + if constexpr (destruct != nullptr) { + connect(this, &QWaylandClientExtensionTemplate::activeChanged, this, [this] { + if (!isActive()) { + std::invoke(destruct, static_cast<T *>(this)); + } + }); + } + } + + ~QWaylandClientExtensionTemplate() + { + if constexpr (destruct != nullptr) { + if (isActive()) { + std::invoke(destruct, static_cast<T *>(this)); + } + } } const struct wl_interface *extensionInterface() const override diff --git a/src/client/global/qwaylandclientextension_p.h b/src/client/global/qwaylandclientextension_p.h index 69cc46a0a..b3ef87a5d 100644 --- a/src/client/global/qwaylandclientextension_p.h +++ b/src/client/global/qwaylandclientextension_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 Erik Larsson. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWaylandCompositor module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 Erik Larsson. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDCLIENTEXTENSION_P_H #define QWAYLANDCLIENTEXTENSION_P_H @@ -53,24 +17,26 @@ #include <QtCore/private/qobject_p.h> #include <QtWaylandClient/QWaylandClientExtension> +#include <QtWaylandClient/private/qwaylanddisplay_p.h> #include <QtWaylandClient/private/qwaylandintegration_p.h> QT_BEGIN_NAMESPACE -class Q_WAYLAND_CLIENT_EXPORT QWaylandClientExtensionPrivate : public QObjectPrivate +class Q_WAYLANDCLIENT_EXPORT QWaylandClientExtensionPrivate : public QObjectPrivate { - Q_DECLARE_PUBLIC(QWaylandClientExtension) public: + Q_DECLARE_PUBLIC(QWaylandClientExtension) QWaylandClientExtensionPrivate(); - static void handleRegistryGlobal(void *data, ::wl_registry *registry, uint32_t id, - const QString &interface, uint32_t version); + + void globalAdded(const QtWaylandClient::QWaylandDisplay::RegistryGlobal &global); + void globalRemoved(const QtWaylandClient::QWaylandDisplay::RegistryGlobal &global); QtWaylandClient::QWaylandIntegration *waylandIntegration = nullptr; int version = -1; bool active = false; }; -class Q_WAYLAND_CLIENT_EXPORT QWaylandClientExtensionTemplatePrivate : public QWaylandClientExtensionPrivate +class Q_WAYLANDCLIENT_EXPORT QWaylandClientExtensionTemplatePrivate : public QWaylandClientExtensionPrivate { public: QWaylandClientExtensionTemplatePrivate() diff --git a/src/client/hardwareintegration/qwaylandclientbufferintegration.cpp b/src/client/hardwareintegration/qwaylandclientbufferintegration.cpp index 61a2c1cdb..b521521b8 100644 --- a/src/client/hardwareintegration/qwaylandclientbufferintegration.cpp +++ b/src/client/hardwareintegration/qwaylandclientbufferintegration.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandclientbufferintegration_p.h" diff --git a/src/client/hardwareintegration/qwaylandclientbufferintegration_p.h b/src/client/hardwareintegration/qwaylandclientbufferintegration_p.h index 7776c6158..e8b78c52b 100644 --- a/src/client/hardwareintegration/qwaylandclientbufferintegration_p.h +++ b/src/client/hardwareintegration/qwaylandclientbufferintegration_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDCLIENTBUFFERINTEGRATION_H #define QWAYLANDCLIENTBUFFERINTEGRATION_H @@ -51,7 +15,7 @@ // We mean it. // -#include <QtCore/qglobal.h> +#include <QtCore/private/qglobal_p.h> #include <QtWaylandClient/qtwaylandclientglobal.h> QT_BEGIN_NAMESPACE @@ -65,7 +29,7 @@ namespace QtWaylandClient { class QWaylandWindow; class QWaylandDisplay; -class Q_WAYLAND_CLIENT_EXPORT QWaylandClientBufferIntegration +class Q_WAYLANDCLIENT_EXPORT QWaylandClientBufferIntegration { public: QWaylandClientBufferIntegration(); diff --git a/src/client/hardwareintegration/qwaylandclientbufferintegrationfactory.cpp b/src/client/hardwareintegration/qwaylandclientbufferintegrationfactory.cpp index 02bed461a..65c7a450d 100644 --- a/src/client/hardwareintegration/qwaylandclientbufferintegrationfactory.cpp +++ b/src/client/hardwareintegration/qwaylandclientbufferintegrationfactory.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandclientbufferintegrationfactory_p.h" #include "qwaylandclientbufferintegrationplugin_p.h" @@ -48,49 +12,17 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { -#if QT_CONFIG(library) -Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, qwcbifLoader, (QWaylandClientBufferIntegrationFactoryInterface_iid, QLatin1String("/wayland-graphics-integration-client"), Qt::CaseInsensitive)) -Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, directLoader, - (QWaylandClientBufferIntegrationFactoryInterface_iid, QLatin1String(""), Qt::CaseInsensitive)) -#endif -QStringList QWaylandClientBufferIntegrationFactory::keys(const QString &pluginPath) +QStringList QWaylandClientBufferIntegrationFactory::keys() { -#if QT_CONFIG(library) - QStringList list; - if (!pluginPath.isEmpty()) { - QCoreApplication::addLibraryPath(pluginPath); - list = directLoader()->keyMap().values(); - if (!list.isEmpty()) { - const QString postFix = QStringLiteral(" (from ") - + QDir::toNativeSeparators(pluginPath) - + QLatin1Char(')'); - const QStringList::iterator end = list.end(); - for (QStringList::iterator it = list.begin(); it != end; ++it) - (*it).append(postFix); - } - } - list.append(loader()->keyMap().values()); - return list; -#else - return QStringList(); -#endif + return qwcbifLoader->keyMap().values(); } -QWaylandClientBufferIntegration *QWaylandClientBufferIntegrationFactory::create(const QString &name, const QStringList &args, const QString &pluginPath) +QWaylandClientBufferIntegration *QWaylandClientBufferIntegrationFactory::create(const QString &name, const QStringList &args) { -#if QT_CONFIG(library) - // Try loading the plugin from platformPluginPath first: - if (!pluginPath.isEmpty()) { - QCoreApplication::addLibraryPath(pluginPath); - if (QWaylandClientBufferIntegration *ret = qLoadPlugin<QWaylandClientBufferIntegration, QWaylandClientBufferIntegrationPlugin>(directLoader(), name, args)) - return ret; - } - if (QWaylandClientBufferIntegration *ret = qLoadPlugin<QWaylandClientBufferIntegration, QWaylandClientBufferIntegrationPlugin>(loader(), name, args)) - return ret; -#endif - return nullptr; + return qLoadPlugin<QWaylandClientBufferIntegration, QWaylandClientBufferIntegrationPlugin>(qwcbifLoader(), name, args); } } diff --git a/src/client/hardwareintegration/qwaylandclientbufferintegrationfactory_p.h b/src/client/hardwareintegration/qwaylandclientbufferintegrationfactory_p.h index 7eaeed16c..2344df59c 100644 --- a/src/client/hardwareintegration/qwaylandclientbufferintegrationfactory_p.h +++ b/src/client/hardwareintegration/qwaylandclientbufferintegrationfactory_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDCLIENTBUFFERINTEGRATIONFACTORY_H #define QWAYLANDCLIENTBUFFERINTEGRATIONFACTORY_H @@ -53,6 +17,7 @@ #include <QtWaylandClient/qtwaylandclientglobal.h> #include <QtCore/QStringList> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -60,11 +25,11 @@ namespace QtWaylandClient { class QWaylandClientBufferIntegration; -class Q_WAYLAND_CLIENT_EXPORT QWaylandClientBufferIntegrationFactory +class Q_WAYLANDCLIENT_EXPORT QWaylandClientBufferIntegrationFactory { public: - static QStringList keys(const QString &pluginPath = QString()); - static QWaylandClientBufferIntegration *create(const QString &name, const QStringList &args, const QString &pluginPath = QString()); + static QStringList keys(); + static QWaylandClientBufferIntegration *create(const QString &name, const QStringList &args); }; } diff --git a/src/client/hardwareintegration/qwaylandclientbufferintegrationplugin.cpp b/src/client/hardwareintegration/qwaylandclientbufferintegrationplugin.cpp index 7aae1d0c4..0335a9219 100644 --- a/src/client/hardwareintegration/qwaylandclientbufferintegrationplugin.cpp +++ b/src/client/hardwareintegration/qwaylandclientbufferintegrationplugin.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandclientbufferintegrationplugin_p.h" @@ -55,3 +19,5 @@ QWaylandClientBufferIntegrationPlugin::~QWaylandClientBufferIntegrationPlugin() } QT_END_NAMESPACE + +#include "moc_qwaylandclientbufferintegrationplugin_p.cpp" diff --git a/src/client/hardwareintegration/qwaylandclientbufferintegrationplugin_p.h b/src/client/hardwareintegration/qwaylandclientbufferintegrationplugin_p.h index e66021916..0cedabff3 100644 --- a/src/client/hardwareintegration/qwaylandclientbufferintegrationplugin_p.h +++ b/src/client/hardwareintegration/qwaylandclientbufferintegrationplugin_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDCLIENTBUFFERINTEGRATIONPLUGIN_H #define QWAYLANDCLIENTBUFFERINTEGRATIONPLUGIN_H @@ -56,6 +20,7 @@ #include <QtCore/qplugin.h> #include <QtCore/qfactoryinterface.h> #include <QtCore/QObject> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -65,7 +30,7 @@ class QWaylandClientBufferIntegration; #define QWaylandClientBufferIntegrationFactoryInterface_iid "org.qt-project.Qt.WaylandClient.QWaylandClientBufferIntegrationFactoryInterface.5.3" -class Q_WAYLAND_CLIENT_EXPORT QWaylandClientBufferIntegrationPlugin : public QObject +class Q_WAYLANDCLIENT_EXPORT QWaylandClientBufferIntegrationPlugin : public QObject { Q_OBJECT public: diff --git a/src/client/hardwareintegration/qwaylandhardwareintegration.cpp b/src/client/hardwareintegration/qwaylandhardwareintegration.cpp index 04340da14..a8f59a7a5 100644 --- a/src/client/hardwareintegration/qwaylandhardwareintegration.cpp +++ b/src/client/hardwareintegration/qwaylandhardwareintegration.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandhardwareintegration_p.h" diff --git a/src/client/hardwareintegration/qwaylandhardwareintegration_p.h b/src/client/hardwareintegration/qwaylandhardwareintegration_p.h index 31b4209dc..d82253f92 100644 --- a/src/client/hardwareintegration/qwaylandhardwareintegration_p.h +++ b/src/client/hardwareintegration/qwaylandhardwareintegration_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDHARDWAREINTEGRATION_H #define QWAYLANDHARDWAREINTEGRATION_H @@ -53,6 +17,7 @@ #include <QtWaylandClient/private/qwayland-hardware-integration.h> #include <QtWaylandClient/qtwaylandclientglobal.h> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -60,7 +25,7 @@ namespace QtWaylandClient { class QWaylandDisplay; -class Q_WAYLAND_CLIENT_EXPORT QWaylandHardwareIntegration : public QtWayland::qt_hardware_integration +class Q_WAYLANDCLIENT_EXPORT QWaylandHardwareIntegration : public QtWayland::qt_hardware_integration { public: QWaylandHardwareIntegration(struct ::wl_registry *registry, int id); diff --git a/src/client/hardwareintegration/qwaylandserverbufferintegration.cpp b/src/client/hardwareintegration/qwaylandserverbufferintegration.cpp index d966a1919..64fd7686b 100644 --- a/src/client/hardwareintegration/qwaylandserverbufferintegration.cpp +++ b/src/client/hardwareintegration/qwaylandserverbufferintegration.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandserverbufferintegration_p.h" diff --git a/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h b/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h index 6833efd0c..92236a7ff 100644 --- a/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h +++ b/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDSERVERBUFFERINTEGRATION_H #define QWAYLANDSERVERBUFFERINTEGRATION_H @@ -56,6 +20,7 @@ #include <QtWaylandClient/private/qwayland-server-buffer-extension.h> #include <QtWaylandClient/qtwaylandclientglobal.h> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -65,7 +30,7 @@ namespace QtWaylandClient { class QWaylandDisplay; -class Q_WAYLAND_CLIENT_EXPORT QWaylandServerBuffer +class Q_WAYLANDCLIENT_EXPORT QWaylandServerBuffer { public: enum Format { @@ -93,7 +58,7 @@ private: void *m_user_data = nullptr; }; -class Q_WAYLAND_CLIENT_EXPORT QWaylandServerBufferIntegration +class Q_WAYLANDCLIENT_EXPORT QWaylandServerBufferIntegration { public: QWaylandServerBufferIntegration(); diff --git a/src/client/hardwareintegration/qwaylandserverbufferintegrationfactory.cpp b/src/client/hardwareintegration/qwaylandserverbufferintegrationfactory.cpp index 39d65f88d..e30bb4dc0 100644 --- a/src/client/hardwareintegration/qwaylandserverbufferintegrationfactory.cpp +++ b/src/client/hardwareintegration/qwaylandserverbufferintegrationfactory.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandserverbufferintegrationfactory_p.h" #include "qwaylandserverbufferintegrationplugin_p.h" @@ -48,49 +12,17 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { -#if QT_CONFIG(library) -Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, qwsbifLoader, (QWaylandServerBufferIntegrationFactoryInterface_iid, QLatin1String("/wayland-graphics-integration-client"), Qt::CaseInsensitive)) -Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, directLoader, - (QWaylandServerBufferIntegrationFactoryInterface_iid, QLatin1String(""), Qt::CaseInsensitive)) -#endif -QStringList QWaylandServerBufferIntegrationFactory::keys(const QString &pluginPath) +QStringList QWaylandServerBufferIntegrationFactory::keys() { -#if QT_CONFIG(library) - QStringList list; - if (!pluginPath.isEmpty()) { - QCoreApplication::addLibraryPath(pluginPath); - list = directLoader()->keyMap().values(); - if (!list.isEmpty()) { - const QString postFix = QStringLiteral(" (from ") - + QDir::toNativeSeparators(pluginPath) - + QLatin1Char(')'); - const QStringList::iterator end = list.end(); - for (QStringList::iterator it = list.begin(); it != end; ++it) - (*it).append(postFix); - } - } - list.append(loader()->keyMap().values()); - return list; -#else - return QStringList(); -#endif + return qwsbifLoader->keyMap().values(); } -QWaylandServerBufferIntegration *QWaylandServerBufferIntegrationFactory::create(const QString &name, const QStringList &args, const QString &pluginPath) +QWaylandServerBufferIntegration *QWaylandServerBufferIntegrationFactory::create(const QString &name, const QStringList &args) { -#if QT_CONFIG(library) - // Try loading the plugin from platformPluginPath first: - if (!pluginPath.isEmpty()) { - QCoreApplication::addLibraryPath(pluginPath); - if (QWaylandServerBufferIntegration *ret = qLoadPlugin<QWaylandServerBufferIntegration, QWaylandServerBufferIntegrationPlugin>(directLoader(), name, args)) - return ret; - } - if (QWaylandServerBufferIntegration *ret = qLoadPlugin<QWaylandServerBufferIntegration, QWaylandServerBufferIntegrationPlugin>(loader(), name, args)) - return ret; -#endif - return nullptr; + return qLoadPlugin<QWaylandServerBufferIntegration, QWaylandServerBufferIntegrationPlugin>(qwsbifLoader(), name, args); } } diff --git a/src/client/hardwareintegration/qwaylandserverbufferintegrationfactory_p.h b/src/client/hardwareintegration/qwaylandserverbufferintegrationfactory_p.h index 600c24c9b..bce4c45d0 100644 --- a/src/client/hardwareintegration/qwaylandserverbufferintegrationfactory_p.h +++ b/src/client/hardwareintegration/qwaylandserverbufferintegrationfactory_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDSERVERBUFFERINTEGRATIONFACTORY_H #define QWAYLANDSERVERBUFFERINTEGRATIONFACTORY_H @@ -53,6 +17,7 @@ #include <QtWaylandClient/qtwaylandclientglobal.h> #include <QtCore/QStringList> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -60,11 +25,11 @@ namespace QtWaylandClient { class QWaylandServerBufferIntegration; -class Q_WAYLAND_CLIENT_EXPORT QWaylandServerBufferIntegrationFactory +class Q_WAYLANDCLIENT_EXPORT QWaylandServerBufferIntegrationFactory { public: - static QStringList keys(const QString &pluginPath = QString()); - static QWaylandServerBufferIntegration *create(const QString &name, const QStringList &args, const QString &pluginPath = QString()); + static QStringList keys(); + static QWaylandServerBufferIntegration *create(const QString &name, const QStringList &args); }; } diff --git a/src/client/hardwareintegration/qwaylandserverbufferintegrationplugin.cpp b/src/client/hardwareintegration/qwaylandserverbufferintegrationplugin.cpp index cff72e7a3..1a01ca04d 100644 --- a/src/client/hardwareintegration/qwaylandserverbufferintegrationplugin.cpp +++ b/src/client/hardwareintegration/qwaylandserverbufferintegrationplugin.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandserverbufferintegrationplugin_p.h" @@ -54,3 +18,5 @@ QWaylandServerBufferIntegrationPlugin::~QWaylandServerBufferIntegrationPlugin() } QT_END_NAMESPACE + +#include "moc_qwaylandserverbufferintegrationplugin_p.cpp" diff --git a/src/client/hardwareintegration/qwaylandserverbufferintegrationplugin_p.h b/src/client/hardwareintegration/qwaylandserverbufferintegrationplugin_p.h index b5e7ede47..6be8ca8bf 100644 --- a/src/client/hardwareintegration/qwaylandserverbufferintegrationplugin_p.h +++ b/src/client/hardwareintegration/qwaylandserverbufferintegrationplugin_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDSERVERBUFFERINTEGRATIONPLUGIN_H #define QWAYLANDSERVERBUFFERINTEGRATIONPLUGIN_H @@ -56,6 +20,7 @@ #include <QtCore/qplugin.h> #include <QtCore/qfactoryinterface.h> #include <QtCore/QObject> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -65,7 +30,7 @@ class QWaylandServerBufferIntegration; #define QWaylandServerBufferIntegrationFactoryInterface_iid "org.qt-project.Qt.WaylandClient.QWaylandServerBufferIntegrationFactoryInterface.5.3" -class Q_WAYLAND_CLIENT_EXPORT QWaylandServerBufferIntegrationPlugin : public QObject +class Q_WAYLANDCLIENT_EXPORT QWaylandServerBufferIntegrationPlugin : public QObject { Q_OBJECT public: diff --git a/src/client/inputdeviceintegration/qwaylandinputdeviceintegration_p.h b/src/client/inputdeviceintegration/qwaylandinputdeviceintegration_p.h index 1fa0fd6de..9a6c8df0b 100644 --- a/src/client/inputdeviceintegration/qwaylandinputdeviceintegration_p.h +++ b/src/client/inputdeviceintegration/qwaylandinputdeviceintegration_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 LG Electronics Ltd -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 LG Electronics Ltd +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDINPUTDEVICEINTEGRATION_H #define QWAYLANDINPUTDEVICEINTEGRATION_H @@ -51,7 +15,7 @@ // We mean it. // -#include <QtCore/qglobal.h> +#include <QtCore/private/qglobal_p.h> #include <QtWaylandClient/qtwaylandclientglobal.h> #include <stdint.h> @@ -63,7 +27,7 @@ namespace QtWaylandClient { class QWaylandDisplay; class QWaylandInputDevice; -class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDeviceIntegration +class Q_WAYLANDCLIENT_EXPORT QWaylandInputDeviceIntegration { public: QWaylandInputDeviceIntegration() {} diff --git a/src/client/inputdeviceintegration/qwaylandinputdeviceintegrationfactory.cpp b/src/client/inputdeviceintegration/qwaylandinputdeviceintegrationfactory.cpp index 8f573064e..1c8eb213d 100644 --- a/src/client/inputdeviceintegration/qwaylandinputdeviceintegrationfactory.cpp +++ b/src/client/inputdeviceintegration/qwaylandinputdeviceintegrationfactory.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 LG Electronics Ltd -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 LG Electronics Ltd +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandinputdeviceintegrationfactory_p.h" #include "qwaylandinputdeviceintegrationplugin_p.h" @@ -48,49 +12,17 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { -#if QT_CONFIG(library) -Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, qwidfiLoader, (QWaylandInputDeviceIntegrationFactoryInterface_iid, QLatin1String("/wayland-inputdevice-integration"), Qt::CaseInsensitive)) -Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, directLoader, - (QWaylandInputDeviceIntegrationFactoryInterface_iid, QLatin1String(""), Qt::CaseInsensitive)) -#endif -QStringList QWaylandInputDeviceIntegrationFactory::keys(const QString &pluginPath) +QStringList QWaylandInputDeviceIntegrationFactory::keys() { -#if QT_CONFIG(library) - QStringList list; - if (!pluginPath.isEmpty()) { - QCoreApplication::addLibraryPath(pluginPath); - list = directLoader()->keyMap().values(); - if (!list.isEmpty()) { - const QString postFix = QStringLiteral(" (from ") - + QDir::toNativeSeparators(pluginPath) - + QLatin1Char(')'); - const QStringList::iterator end = list.end(); - for (QStringList::iterator it = list.begin(); it != end; ++it) - (*it).append(postFix); - } - } - list.append(loader()->keyMap().values()); - return list; -#else - return QStringList(); -#endif + return qwidfiLoader->keyMap().values(); } -QWaylandInputDeviceIntegration *QWaylandInputDeviceIntegrationFactory::create(const QString &name, const QStringList &args, const QString &pluginPath) +QWaylandInputDeviceIntegration *QWaylandInputDeviceIntegrationFactory::create(const QString &name, const QStringList &args) { -#if QT_CONFIG(library) - // Try loading the plugin from platformPluginPath first: - if (!pluginPath.isEmpty()) { - QCoreApplication::addLibraryPath(pluginPath); - if (QWaylandInputDeviceIntegration *ret = qLoadPlugin<QWaylandInputDeviceIntegration, QWaylandInputDeviceIntegrationPlugin>(directLoader(), name, args)) - return ret; - } - if (QWaylandInputDeviceIntegration *ret = qLoadPlugin<QWaylandInputDeviceIntegration, QWaylandInputDeviceIntegrationPlugin>(loader(), name, args)) - return ret; -#endif - return nullptr; + return qLoadPlugin<QWaylandInputDeviceIntegration, QWaylandInputDeviceIntegrationPlugin>(qwidfiLoader(), name, args); } } diff --git a/src/client/inputdeviceintegration/qwaylandinputdeviceintegrationfactory_p.h b/src/client/inputdeviceintegration/qwaylandinputdeviceintegrationfactory_p.h index 80096e790..087c963e1 100644 --- a/src/client/inputdeviceintegration/qwaylandinputdeviceintegrationfactory_p.h +++ b/src/client/inputdeviceintegration/qwaylandinputdeviceintegrationfactory_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 LG Electronics Ltd -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 LG Electronics Ltd +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDINPUTDEVICEINTEGRATIONFACTORY_H #define QWAYLANDINPUTDEVICEINTEGRATIONFACTORY_H @@ -53,6 +17,7 @@ #include <QtWaylandClient/qtwaylandclientglobal.h> #include <QtCore/QStringList> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -60,11 +25,11 @@ namespace QtWaylandClient { class QWaylandInputDeviceIntegration; -class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDeviceIntegrationFactory +class Q_WAYLANDCLIENT_EXPORT QWaylandInputDeviceIntegrationFactory { public: - static QStringList keys(const QString &pluginPath = QString()); - static QWaylandInputDeviceIntegration *create(const QString &name, const QStringList &args, const QString &pluginPath = QString()); + static QStringList keys(); + static QWaylandInputDeviceIntegration *create(const QString &name, const QStringList &args); }; } diff --git a/src/client/inputdeviceintegration/qwaylandinputdeviceintegrationplugin.cpp b/src/client/inputdeviceintegration/qwaylandinputdeviceintegrationplugin.cpp index 579095767..424b44d3f 100644 --- a/src/client/inputdeviceintegration/qwaylandinputdeviceintegrationplugin.cpp +++ b/src/client/inputdeviceintegration/qwaylandinputdeviceintegrationplugin.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 LG Electronics Ltd -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 LG Electronics Ltd +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandinputdeviceintegrationplugin_p.h" @@ -55,3 +19,5 @@ QWaylandInputDeviceIntegrationPlugin::~QWaylandInputDeviceIntegrationPlugin() } QT_END_NAMESPACE + +#include "moc_qwaylandinputdeviceintegrationplugin_p.cpp" diff --git a/src/client/inputdeviceintegration/qwaylandinputdeviceintegrationplugin_p.h b/src/client/inputdeviceintegration/qwaylandinputdeviceintegrationplugin_p.h index bae6e2166..e43ce1bd1 100644 --- a/src/client/inputdeviceintegration/qwaylandinputdeviceintegrationplugin_p.h +++ b/src/client/inputdeviceintegration/qwaylandinputdeviceintegrationplugin_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 LG Electronics Ltd -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 LG Electronics Ltd +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDINPUTDEVICEINTEGRATIONPLUGIN_H #define QWAYLANDINPUTDEVICEINTEGRATIONPLUGIN_H @@ -56,6 +20,7 @@ #include <QtCore/qplugin.h> #include <QtCore/qfactoryinterface.h> #include <QtCore/QObject> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -65,7 +30,7 @@ class QWaylandInputDeviceIntegration; #define QWaylandInputDeviceIntegrationFactoryInterface_iid "org.qt-project.Qt.WaylandClient.QWaylandInputDeviceIntegrationFactoryInterface.5.3" -class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDeviceIntegrationPlugin : public QObject +class Q_WAYLANDCLIENT_EXPORT QWaylandInputDeviceIntegrationPlugin : public QObject { Q_OBJECT public: diff --git a/src/client/qt_cmdline.cmake b/src/client/qt_cmdline.cmake new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/client/qt_cmdline.cmake diff --git a/src/client/qtwaylandclientglobal.h b/src/client/qtwaylandclientglobal.h index 5f474f378..0e5475294 100644 --- a/src/client/qtwaylandclientglobal.h +++ b/src/client/qtwaylandclientglobal.h @@ -1,69 +1,15 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDCLIENTGLOBAL_H #define QWAYLANDCLIENTGLOBAL_H -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - #include <QtGui/qtguiglobal.h> #include <QtWaylandClient/qtwaylandclient-config.h> +#include <QtWaylandClient/qtwaylandclientexports.h> QT_BEGIN_NAMESPACE -#if !defined(Q_WAYLAND_CLIENT_EXPORT) -# if defined(QT_SHARED) -# define Q_WAYLAND_CLIENT_EXPORT Q_DECL_EXPORT -# else -# define Q_WAYLAND_CLIENT_EXPORT -# endif -#endif - QT_END_NAMESPACE #endif //QWAYLANDCLIENTGLOBAL_H diff --git a/src/client/qtwaylandclientglobal_p.h b/src/client/qtwaylandclientglobal_p.h index f2106d0b8..f98d143d2 100644 --- a/src/client/qtwaylandclientglobal_p.h +++ b/src/client/qtwaylandclientglobal_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDCLIENTGLOBAL_P_H #define QWAYLANDCLIENTGLOBAL_P_H @@ -51,9 +15,9 @@ // We mean it. // +#include <QtWaylandGlobal/private/qtwaylandglobal-config_p.h> #include <QtWaylandClient/qtwaylandclientglobal.h> #include <QtGui/private/qtguiglobal_p.h> #include <QtWaylandClient/private/qtwaylandclient-config_p.h> #endif //QWAYLANDCLIENTGLOBAL_P_H - diff --git a/src/client/qwaylandabstractdecoration.cpp b/src/client/qwaylandabstractdecoration.cpp index 87dd6cea0..699618f68 100644 --- a/src/client/qwaylandabstractdecoration.cpp +++ b/src/client/qwaylandabstractdecoration.cpp @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net> -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandabstractdecoration_p.h" @@ -108,11 +72,11 @@ void QWaylandAbstractDecoration::setWaylandWindow(QWaylandWindow *window) static QRegion marginsRegion(const QSize &size, const QMargins &margins) { QRegion r; - const int widthWithMargins = margins.left() + size.width() + margins.right(); - r += QRect(0, 0, widthWithMargins, margins.top()); // top - r += QRect(0, size.height()+margins.top(), widthWithMargins, margins.bottom()); //bottom + + r += QRect(0, 0, size.width(), margins.top()); // top + r += QRect(0, size.height()-margins.bottom(), size.width(), margins.bottom()); //bottom r += QRect(0, margins.top(), margins.left(), size.height()); //left - r += QRect(size.width()+margins.left(), margins.top(), margins.right(), size.height()); // right + r += QRect(size.width()-margins.left(), margins.top(), margins.right(), size.height()-margins.top()); // right return r; } @@ -122,7 +86,7 @@ const QImage &QWaylandAbstractDecoration::contentImage() if (d->m_isDirty) { // Update the decoration backingstore - const int bufferScale = waylandWindow()->scale(); + const qreal bufferScale = waylandWindow()->scale(); const QSize imageSize = waylandWindow()->surfaceSize() * bufferScale; d->m_decorationContentImage = QImage(imageSize, QImage::Format_ARGB32_Premultiplied); // Only scale by buffer scale, not QT_SCALE_FACTOR etc. @@ -216,3 +180,5 @@ QWaylandWindow *QWaylandAbstractDecoration::waylandWindow() const } QT_END_NAMESPACE + +#include "moc_qwaylandabstractdecoration_p.cpp" diff --git a/src/client/qwaylandabstractdecoration_p.h b/src/client/qwaylandabstractdecoration_p.h index 81c8e1771..3334e00c1 100644 --- a/src/client/qwaylandabstractdecoration_p.h +++ b/src/client/qwaylandabstractdecoration_p.h @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net> -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 Robin Burchell <robin.burchell@viroteck.net> +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDABSTRACTDECORATION_H #define QWAYLANDABSTRACTDECORATION_H @@ -59,9 +23,11 @@ #include <QtGui/QColor> #include <QtGui/QStaticText> #include <QtGui/QImage> +#include <QtGui/QEventPoint> #include <QtWaylandClient/qtwaylandclientglobal.h> #include <QtCore/QDebug> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -77,11 +43,17 @@ class QWaylandWindow; class QWaylandInputDevice; class QWaylandAbstractDecorationPrivate; -class Q_WAYLAND_CLIENT_EXPORT QWaylandAbstractDecoration : public QObject +class Q_WAYLANDCLIENT_EXPORT QWaylandAbstractDecoration : public QObject { Q_OBJECT Q_DECLARE_PRIVATE(QWaylandAbstractDecoration) public: + enum MarginsType { + Full, + ShadowsExcluded, + ShadowsOnly + }; + QWaylandAbstractDecoration(); ~QWaylandAbstractDecoration() override; @@ -91,12 +63,13 @@ public: void update(); bool isDirty() const; - virtual QMargins margins() const = 0; + virtual QMargins margins(MarginsType marginsType = Full) const = 0; + QWindow *window() const; const QImage &contentImage(); virtual bool handleMouse(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global,Qt::MouseButtons b,Qt::KeyboardModifiers mods) = 0; - virtual bool handleTouch(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, Qt::TouchPointState state, Qt::KeyboardModifiers mods) = 0; + virtual bool handleTouch(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, QEventPoint::State state, Qt::KeyboardModifiers mods) = 0; protected: virtual void paint(QPaintDevice *device) = 0; diff --git a/src/client/qwaylandbuffer.cpp b/src/client/qwaylandbuffer.cpp index 12df9cc4f..dd99b702b 100644 --- a/src/client/qwaylandbuffer.cpp +++ b/src/client/qwaylandbuffer.cpp @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Copyright (C) 2017 Giulio Camuffo. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2017 Giulio Camuffo. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandbuffer_p.h" @@ -67,6 +31,13 @@ void QWaylandBuffer::release(void *data, wl_buffer *) QWaylandBuffer *self = static_cast<QWaylandBuffer *>(data); self->mBusy = false; self->mCommitted = false; + if (self->mDeleteOnRelease) + delete self; +} + +void QWaylandBuffer::setDeleteOnRelease(bool deleteOnRelease) +{ + mDeleteOnRelease = deleteOnRelease; } const wl_buffer_listener QWaylandBuffer::listener = { diff --git a/src/client/qwaylandbuffer_p.h b/src/client/qwaylandbuffer_p.h index 945f1279a..c96f213b9 100644 --- a/src/client/qwaylandbuffer_p.h +++ b/src/client/qwaylandbuffer_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDBUFFER_H #define QWAYLANDBUFFER_H @@ -57,12 +21,13 @@ #include <QtCore/QRect> #include <QtWaylandClient/private/wayland-wayland-client-protocol.h> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE namespace QtWaylandClient { -class Q_WAYLAND_CLIENT_EXPORT QWaylandBuffer { +class Q_WAYLANDCLIENT_EXPORT QWaylandBuffer { public: QWaylandBuffer(); virtual ~QWaylandBuffer(); @@ -72,18 +37,21 @@ public: virtual QSize size() const = 0; virtual int scale() const { return 1; } - void setBusy() { mBusy = true; } + void setBusy(bool busy) { mBusy = busy; } bool busy() const { return mBusy; } void setCommitted() { mCommitted = true; } bool committed() const { return mCommitted; } + void setDeleteOnRelease(bool deleteOnRelease); + protected: struct wl_buffer *mBuffer = nullptr; private: bool mBusy = false; bool mCommitted = false; + bool mDeleteOnRelease = false; static void release(void *data, wl_buffer *); static const wl_buffer_listener listener; diff --git a/src/client/qwaylandclipboard.cpp b/src/client/qwaylandclipboard.cpp index 81f48e05e..df6cf5d2b 100644 --- a/src/client/qwaylandclipboard.cpp +++ b/src/client/qwaylandclipboard.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandclipboard_p.h" #include "qwaylanddisplay_p.h" @@ -54,10 +18,15 @@ namespace QtWaylandClient { QWaylandClipboard::QWaylandClipboard(QWaylandDisplay *display) : mDisplay(display) { + m_clientClipboard[QClipboard::Clipboard] = nullptr; + m_clientClipboard[QClipboard::Selection] = nullptr; } QWaylandClipboard::~QWaylandClipboard() { + if (m_clientClipboard[QClipboard::Clipboard] != m_clientClipboard[QClipboard::Selection]) + delete m_clientClipboard[QClipboard::Clipboard]; + delete m_clientClipboard[QClipboard::Selection]; } QMimeData *QWaylandClipboard::mimeData(QClipboard::Mode mode) @@ -69,8 +38,8 @@ QMimeData *QWaylandClipboard::mimeData(QClipboard::Mode mode) switch (mode) { case QClipboard::Clipboard: if (auto *dataDevice = seat->dataDevice()) { - if (auto *source = dataDevice->selectionSource()) - return source->mimeData(); + if (dataDevice->selectionSource()) + return m_clientClipboard[QClipboard::Clipboard]; if (auto *offer = dataDevice->selectionOffer()) return offer->mimeData(); } @@ -78,8 +47,8 @@ QMimeData *QWaylandClipboard::mimeData(QClipboard::Mode mode) case QClipboard::Selection: #if QT_CONFIG(wayland_client_primary_selection) if (auto *selectionDevice = seat->primarySelectionDevice()) { - if (auto *source = selectionDevice->selectionSource()) - return source->mimeData(); + if (selectionDevice->selectionSource()) + return m_clientClipboard[QClipboard::Selection]; if (auto *offer = selectionDevice->selectionOffer()) return offer->mimeData(); } @@ -104,17 +73,27 @@ void QWaylandClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) if (data && data->hasFormat(plain) && !data->hasFormat(utf8)) data->setData(utf8, data->data(plain)); + if (m_clientClipboard[mode]) { + if (m_clientClipboard[QClipboard::Clipboard] != m_clientClipboard[QClipboard::Selection]) + delete m_clientClipboard[mode]; + m_clientClipboard[mode] = nullptr; + } + + m_clientClipboard[mode] = data; + switch (mode) { case QClipboard::Clipboard: if (auto *dataDevice = seat->dataDevice()) { - dataDevice->setSelectionSource(data ? new QWaylandDataSource(mDisplay->dndSelectionHandler(), data) : nullptr); + dataDevice->setSelectionSource(data ? new QWaylandDataSource(mDisplay->dndSelectionHandler(), + m_clientClipboard[QClipboard::Clipboard]) : nullptr); emitChanged(mode); } break; case QClipboard::Selection: #if QT_CONFIG(wayland_client_primary_selection) if (auto *selectionDevice = seat->primarySelectionDevice()) { - selectionDevice->setSelectionSource(data ? new QWaylandPrimarySelectionSourceV1(mDisplay->primarySelectionManager(), data) : nullptr); + selectionDevice->setSelectionSource(data ? new QWaylandPrimarySelectionSourceV1(mDisplay->primarySelectionManager(), + m_clientClipboard[QClipboard::Selection]) : nullptr); emitChanged(mode); } #endif diff --git a/src/client/qwaylandclipboard_p.h b/src/client/qwaylandclipboard_p.h index ce14e1240..414e3dc71 100644 --- a/src/client/qwaylandclipboard_p.h +++ b/src/client/qwaylandclipboard_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDCLIPBOARD_H #define QWAYLANDCLIPBOARD_H @@ -56,6 +20,7 @@ #include <QtCore/QMimeData> #include <QtWaylandClient/qtwaylandclientglobal.h> +#include <QtCore/private/qglobal_p.h> QT_REQUIRE_CONFIG(clipboard); @@ -65,7 +30,7 @@ namespace QtWaylandClient { class QWaylandDisplay; -class Q_WAYLAND_CLIENT_EXPORT QWaylandClipboard : public QPlatformClipboard +class Q_WAYLANDCLIENT_EXPORT QWaylandClipboard : public QPlatformClipboard { public: QWaylandClipboard(QWaylandDisplay *display); @@ -80,6 +45,7 @@ public: private: QWaylandDisplay *mDisplay = nullptr; QMimeData m_emptyData; + QMimeData *m_clientClipboard[2]; }; } diff --git a/src/client/qwaylandcursor.cpp b/src/client/qwaylandcursor.cpp index 4356b23a0..98d51a842 100644 --- a/src/client/qwaylandcursor.cpp +++ b/src/client/qwaylandcursor.cpp @@ -1,41 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2023 David Edmundson <davidedmundson@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandcursor_p.h" @@ -43,6 +8,9 @@ #include "qwaylandinputdevice_p.h" #include "qwaylandshmbackingstore_p.h" +#include <QtGui/private/qguiapplication_p.h> +#include <qpa/qplatformtheme.h> + #include <QtGui/QImageReader> #include <QDebug> @@ -54,7 +22,7 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { -QWaylandCursorTheme *QWaylandCursorTheme::create(QWaylandShm *shm, int size, const QString &themeName) +std::unique_ptr<QWaylandCursorTheme> QWaylandCursorTheme::create(QWaylandShm *shm, int size, const QString &themeName) { QByteArray nameBytes = themeName.toLocal8Bit(); struct ::wl_cursor_theme *theme = wl_cursor_theme_load(nameBytes.constData(), size, shm->object()); @@ -64,7 +32,7 @@ QWaylandCursorTheme *QWaylandCursorTheme::create(QWaylandShm *shm, int size, con return nullptr; } - return new QWaylandCursorTheme(theme); + return std::unique_ptr<QWaylandCursorTheme>{new QWaylandCursorTheme(theme)}; } QWaylandCursorTheme::~QWaylandCursorTheme() @@ -74,7 +42,7 @@ QWaylandCursorTheme::~QWaylandCursorTheme() wl_cursor *QWaylandCursorTheme::requestCursor(WaylandCursor shape) { - if (struct wl_cursor *cursor = m_cursors.value(shape, nullptr)) + if (struct wl_cursor *cursor = m_cursors[shape]) return cursor; static Q_CONSTEXPR struct ShapeAndName { @@ -120,6 +88,8 @@ wl_cursor *QWaylandCursorTheme::requestCursor(WaylandCursor shape) {SizeAllCursor, "size_all"}, + {BlankCursor, "blank"}, + {SplitVCursor, "split_v"}, {SplitVCursor, "row-resize"}, {SplitVCursor, "sb_v_double_arrow"}, @@ -206,7 +176,7 @@ wl_cursor *QWaylandCursorTheme::requestCursor(WaylandCursor shape) ShapeAndName{shape, ""}, byShape); for (auto it = p.first; it != p.second; ++it) { if (wl_cursor *cursor = wl_cursor_theme_get_cursor(m_theme, it->name)) { - m_cursors.insert(shape, cursor); + m_cursors[shape] = cursor; return cursor; } } @@ -219,7 +189,7 @@ wl_cursor *QWaylandCursorTheme::requestCursor(WaylandCursor shape) return nullptr; } -::wl_cursor_image *QWaylandCursorTheme::cursorImage(Qt::CursorShape shape, uint millisecondsIntoAnimation) +::wl_cursor *QWaylandCursorTheme::cursor(Qt::CursorShape shape) { struct wl_cursor *waylandCursor = nullptr; @@ -237,15 +207,78 @@ wl_cursor *QWaylandCursorTheme::requestCursor(WaylandCursor shape) return nullptr; } - int frame = wl_cursor_frame(waylandCursor, millisecondsIntoAnimation); - ::wl_cursor_image *image = waylandCursor->images[frame]; - ::wl_buffer *buffer = wl_cursor_image_get_buffer(image); - if (!buffer) { - qCWarning(lcQpaWayland) << "Could not find buffer for cursor"; - return nullptr; + return waylandCursor; +} + +QWaylandCursorShape::QWaylandCursorShape(::wp_cursor_shape_device_v1 *object) + : QtWayland::wp_cursor_shape_device_v1(object) +{} + +QWaylandCursorShape::~QWaylandCursorShape() +{ + destroy(); +} + +static QtWayland::wp_cursor_shape_device_v1::shape qtCursorShapeToWaylandShape(Qt::CursorShape cursorShape) +{ + using QtWayland::wp_cursor_shape_device_v1; + + switch (cursorShape) { + case Qt::BlankCursor: + case Qt::CustomCursor: + case Qt::BitmapCursor: + // these should have been handled separately before using the shape protocol + Q_ASSERT(false); + break; + case Qt::ArrowCursor: + return wp_cursor_shape_device_v1::shape_default; + case Qt::SizeVerCursor: + return wp_cursor_shape_device_v1::shape_ns_resize; + case Qt::UpArrowCursor: + return wp_cursor_shape_device_v1::shape_n_resize; + case Qt::SizeHorCursor: + return wp_cursor_shape_device_v1::shape_ew_resize; + case Qt::CrossCursor: + return wp_cursor_shape_device_v1::shape_crosshair; + case Qt::SizeBDiagCursor: + return wp_cursor_shape_device_v1::shape_nesw_resize; + case Qt::IBeamCursor: + return wp_cursor_shape_device_v1::shape_text; + case Qt::SizeFDiagCursor: + return wp_cursor_shape_device_v1::shape_nwse_resize; + case Qt::WaitCursor: + return wp_cursor_shape_device_v1::shape_wait; + case Qt::SizeAllCursor: + return wp_cursor_shape_device_v1::shape_all_scroll; + case Qt::BusyCursor: + return wp_cursor_shape_device_v1::shape_progress; + case Qt::SplitVCursor: + return wp_cursor_shape_device_v1::shape_row_resize; + case Qt::ForbiddenCursor: + return wp_cursor_shape_device_v1::shape_not_allowed; + case Qt::SplitHCursor: + return wp_cursor_shape_device_v1::shape_col_resize; + case Qt::PointingHandCursor: + return wp_cursor_shape_device_v1::shape_pointer; + case Qt::OpenHandCursor: + return wp_cursor_shape_device_v1::shape_grab; + case Qt::WhatsThisCursor: + return wp_cursor_shape_device_v1::shape_help; + case Qt::ClosedHandCursor: + return wp_cursor_shape_device_v1::shape_grabbing; + case Qt::DragMoveCursor: + return wp_cursor_shape_device_v1::shape_move; + case Qt::DragCopyCursor: + return wp_cursor_shape_device_v1::shape_copy; + case Qt::DragLinkCursor: + return wp_cursor_shape_device_v1::shape_alias; } + return wp_cursor_shape_device_v1::shape_default; +} - return image; +void QWaylandCursorShape::setShape(uint32_t serial, Qt::CursorShape shape) +{ + set_shape(serial, qtCursorShapeToWaylandShape(shape)); } QWaylandCursor::QWaylandCursor(QWaylandDisplay *display) @@ -256,10 +289,24 @@ QWaylandCursor::QWaylandCursor(QWaylandDisplay *display) QSharedPointer<QWaylandBuffer> QWaylandCursor::cursorBitmapBuffer(QWaylandDisplay *display, const QCursor *cursor) { Q_ASSERT(cursor->shape() == Qt::BitmapCursor); - const QImage &img = cursor->pixmap().toImage(); + QImage img = !cursor->pixmap().isNull() ? cursor->pixmap().toImage() : cursor->bitmap().toImage(); + + // convert to supported format if necessary + if (!display->shm()->formatSupported(img.format())) { + if (cursor->mask().isNull()) { + img.convertTo(QImage::Format_RGB32); + } else { + // preserve mask + img.convertTo(QImage::Format_ARGB32); + QPixmap pixmap = QPixmap::fromImage(img); + pixmap.setMask(cursor->mask()); + img = pixmap.toImage(); + } + } + QSharedPointer<QWaylandShmBuffer> buffer(new QWaylandShmBuffer(display, img.size(), img.format())); memcpy(buffer->image()->bits(), img.bits(), size_t(img.sizeInBytes())); - return std::move(buffer); + return buffer; } void QWaylandCursor::changeCursor(QCursor *cursor, QWindow *window) @@ -278,7 +325,7 @@ void QWaylandCursor::changeCursor(QCursor *cursor, QWindow *window) void QWaylandCursor::pointerEvent(const QMouseEvent &event) { - mLastPos = event.globalPos(); + mLastPos = event.globalPosition().toPoint(); } QPoint QWaylandCursor::pos() const @@ -292,6 +339,13 @@ void QWaylandCursor::setPos(const QPoint &pos) qCWarning(lcQpaWayland) << "Setting cursor position is not possible on wayland"; } +QSize QWaylandCursor::size() const +{ + if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) + return theme->themeHint(QPlatformTheme::MouseCursorSize).toSize(); + return QSize(24, 24); +} + } // namespace QtWaylandClient QT_END_NAMESPACE diff --git a/src/client/qwaylandcursor_p.h b/src/client/qwaylandcursor_p.h index a4605f3d2..1f6d5109e 100644 --- a/src/client/qwaylandcursor_p.h +++ b/src/client/qwaylandcursor_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDCURSOR_H #define QWAYLANDCURSOR_H @@ -54,9 +18,13 @@ #include <qpa/qplatformcursor.h> #include <QtCore/QMap> #include <QtWaylandClient/qtwaylandclientglobal.h> +#include <QtWaylandClient/private/qwayland-cursor-shape-v1.h> +#include <QtCore/private/qglobal_p.h> #if QT_CONFIG(cursor) +#include <memory> + struct wl_cursor; struct wl_cursor_image; struct wl_cursor_theme; @@ -70,14 +38,14 @@ class QWaylandDisplay; class QWaylandScreen; class QWaylandShm; -class Q_WAYLAND_CLIENT_EXPORT QWaylandCursorTheme +class Q_WAYLANDCLIENT_EXPORT QWaylandCursorTheme { public: - static QWaylandCursorTheme *create(QWaylandShm *shm, int size, const QString &themeName); + static std::unique_ptr<QWaylandCursorTheme> create(QWaylandShm *shm, int size, const QString &themeName); ~QWaylandCursorTheme(); - ::wl_cursor_image *cursorImage(Qt::CursorShape shape, uint millisecondsIntoAnimation = 0); + ::wl_cursor *cursor(Qt::CursorShape shape); -private: +protected: enum WaylandCursor { ArrowCursor = Qt::ArrowCursor, UpArrowCursor, @@ -109,16 +77,26 @@ private: ResizeNorthWestCursor, ResizeSouthEastCursor, ResizeNorthEastCursor, - ResizeSouthWestCursor + ResizeSouthWestCursor, + + NumWaylandCursors }; explicit QWaylandCursorTheme(struct ::wl_cursor_theme *theme) : m_theme(theme) {} struct ::wl_cursor *requestCursor(WaylandCursor shape); struct ::wl_cursor_theme *m_theme = nullptr; - QMap<WaylandCursor, wl_cursor *> m_cursors; + wl_cursor *m_cursors[NumWaylandCursors] = {}; }; -class Q_WAYLAND_CLIENT_EXPORT QWaylandCursor : public QPlatformCursor +class Q_WAYLANDCLIENT_EXPORT QWaylandCursorShape : public QtWayland::wp_cursor_shape_device_v1 +{ +public: + QWaylandCursorShape(struct ::wp_cursor_shape_device_v1 *object); + ~QWaylandCursorShape(); + void setShape(uint32_t serial, Qt::CursorShape shape); +}; + +class Q_WAYLANDCLIENT_EXPORT QWaylandCursor : public QPlatformCursor { public: explicit QWaylandCursor(QWaylandDisplay *display); @@ -128,10 +106,11 @@ public: QPoint pos() const override; void setPos(const QPoint &pos) override; + QSize size() const override; + static QSharedPointer<QWaylandBuffer> cursorBitmapBuffer(QWaylandDisplay *display, const QCursor *cursor); - struct wl_cursor_image *cursorImage(Qt::CursorShape shape); -private: +protected: QWaylandDisplay *mDisplay = nullptr; QPoint mLastPos; }; diff --git a/src/client/qwaylanddatadevice.cpp b/src/client/qwaylanddatadevice.cpp index f7d135e49..30bfb86c5 100644 --- a/src/client/qwaylanddatadevice.cpp +++ b/src/client/qwaylanddatadevice.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Klarälvdalens Datakonsult AB (KDAB). -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWaylandClient module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 Klarälvdalens Datakonsult AB (KDAB). +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylanddatadevice_p.h" @@ -49,6 +13,8 @@ #include "qwaylandabstractdecoration_p.h" #include "qwaylandsurface_p.h" +#include <QtWaylandClient/private/qwayland-xdg-toplevel-drag-v1.h> + #include <QtCore/QMimeData> #include <QtGui/QGuiApplication> #include <QtGui/private/qguiapplication_p.h> @@ -63,8 +29,11 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { +using namespace Qt::StringLiterals; + QWaylandDataDevice::QWaylandDataDevice(QWaylandDataDeviceManager *manager, QWaylandInputDevice *inputDevice) - : QtWayland::wl_data_device(manager->get_data_device(inputDevice->wl_seat())) + : QObject(inputDevice) + , QtWayland::wl_data_device(manager->get_data_device(inputDevice->wl_seat())) , m_display(manager->display()) , m_inputDevice(inputDevice) { @@ -72,6 +41,10 @@ QWaylandDataDevice::QWaylandDataDevice(QWaylandDataDeviceManager *manager, QWayl QWaylandDataDevice::~QWaylandDataDevice() { + if (version() >= WL_DATA_DEVICE_RELEASE_SINCE_VERSION) + release(); + else + wl_data_device_destroy(object()); } QWaylandDataOffer *QWaylandDataDevice::selectionOffer() const @@ -110,7 +83,7 @@ QWaylandDataOffer *QWaylandDataDevice::dragOffer() const return m_dragOffer.data(); } -bool QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon) +bool QWaylandDataDevice::startDrag(QMimeData *mimeData, Qt::DropActions supportedActions, QWaylandWindow *icon) { auto *seat = m_display->currentInputDevice(); auto *origin = seat->pointerFocus(); @@ -122,8 +95,71 @@ bool QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon) return false; } + // dragging data without mimetypes is a legal operation in Qt terms + // but Wayland uses a mimetype to determine if a drag is accepted or not + // In this rare case, insert a placeholder + if (mimeData->formats().isEmpty()) + mimeData->setData("application/x-qt-avoid-empty-placeholder"_L1, QByteArray("1")); + m_dragSource.reset(new QWaylandDataSource(m_display->dndSelectionHandler(), mimeData)); + + if (version() >= 3) + m_dragSource->set_actions(dropActionsToWl(supportedActions)); + connect(m_dragSource.data(), &QWaylandDataSource::cancelled, this, &QWaylandDataDevice::dragSourceCancelled); + connect(m_dragSource.data(), &QWaylandDataSource::dndResponseUpdated, this, [this](bool accepted, Qt::DropAction action) { + auto drag = static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag()); + if (!drag->currentDrag()) { + return; + } + // in old versions drop action is not set, so we guess + if (m_dragSource->version() < 3) { + drag->setResponse(accepted); + } else { + QPlatformDropQtResponse response(accepted, action); + drag->setResponse(response); + } + }); + connect(m_dragSource.data(), &QWaylandDataSource::dndDropped, this, + [this](bool accepted, Qt::DropAction action) { + QPlatformDropQtResponse response(accepted, action); + if (m_toplevelDrag) { + // If the widget was dropped but the drag not accepted it + // should be its own window in the future. To distinguish + // from canceling mid-drag the drag is accepted here as the + // we know if the widget is over a zone where it can be + // incorporated or not + response = { accepted, Qt::MoveAction }; + } + static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag()) + ->setDropResponse(response); + }); + connect(m_dragSource.data(), &QWaylandDataSource::finished, this, [this]() { + static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag(); + if (m_toplevelDrag) { + m_toplevelDrag->destroy(); + m_toplevelDrag = nullptr; + } + }); + + if (mimeData->hasFormat("application/x-qt-mainwindowdrag-window"_L1) + && m_display->xdgToplevelDragManager()) { + qintptr dockWindowPtr; + QPoint offset; + QDataStream windowStream(mimeData->data("application/x-qt-mainwindowdrag-window"_L1)); + windowStream >> dockWindowPtr; + QWindow *dockWindow = reinterpret_cast<QWindow *>(dockWindowPtr); + QDataStream offsetStream(mimeData->data("application/x-qt-mainwindowdrag-position"_L1)); + offsetStream >> offset; + if (auto waylandWindow = static_cast<QWaylandWindow *>(dockWindow->handle())) { + if (auto toplevel = waylandWindow->surfaceRole<xdg_toplevel>()) { + m_toplevelDrag = new QtWayland::xdg_toplevel_drag_v1( + m_display->xdgToplevelDragManager()->get_xdg_toplevel_drag( + m_dragSource->object())); + m_toplevelDrag->attach(toplevel, offset.x(), offset.y()); + } + } + } start_drag(m_dragSource->object(), origin->wlSurface(), icon->wlSurface(), m_display->currentInputDevice()->serial()); return true; @@ -152,21 +188,26 @@ void QWaylandDataDevice::data_device_drop() supportedActions = drag->supportedActions(); } else if (m_dragOffer) { dragData = m_dragOffer->mimeData(); - supportedActions = Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; + supportedActions = m_dragOffer->supportedActions(); } else { return; } - QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(m_dragWindow, dragData, m_dragPoint, supportedActions); - + QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(m_dragWindow, dragData, m_dragPoint, supportedActions, + QGuiApplication::mouseButtons(), + m_inputDevice->modifiers()); if (drag) { - static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag(response); + auto drag = static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag()); + drag->setDropResponse(response); + drag->finishDrag(); + } else if (m_dragOffer) { + m_dragOffer->finish(); } } void QWaylandDataDevice::data_device_enter(uint32_t serial, wl_surface *surface, wl_fixed_t x, wl_fixed_t y, wl_data_offer *id) { - auto *dragWaylandWindow = QWaylandWindow::fromWlSurface(surface); + auto *dragWaylandWindow = surface ? QWaylandWindow::fromWlSurface(surface) : nullptr; if (!dragWaylandWindow) return; // Ignore foreign surfaces @@ -184,26 +225,25 @@ void QWaylandDataDevice::data_device_enter(uint32_t serial, wl_surface *surface, supportedActions = drag->supportedActions(); } else if (m_dragOffer) { dragData = m_dragOffer->mimeData(); - supportedActions = Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; + supportedActions = m_dragOffer->supportedActions(); } - const QPlatformDragQtResponse &response = QWindowSystemInterface::handleDrag(m_dragWindow, dragData, m_dragPoint, supportedActions); - + const QPlatformDragQtResponse &response = QWindowSystemInterface::handleDrag( + m_dragWindow, dragData, m_dragPoint, supportedActions, QGuiApplication::mouseButtons(), + m_inputDevice->modifiers()); if (drag) { static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->setResponse(response); } - if (response.isAccepted()) { - wl_data_offer_accept(m_dragOffer->object(), m_enterSerial, m_dragOffer->firstFormat().toUtf8().constData()); - } else { - wl_data_offer_accept(m_dragOffer->object(), m_enterSerial, nullptr); - } + sendResponse(supportedActions, response); } void QWaylandDataDevice::data_device_leave() { if (m_dragWindow) - QWindowSystemInterface::handleDrag(m_dragWindow, nullptr, QPoint(), Qt::IgnoreAction); + QWindowSystemInterface::handleDrag(m_dragWindow, nullptr, QPoint(), Qt::IgnoreAction, + QGuiApplication::mouseButtons(), + m_inputDevice->modifiers()); QDrag *drag = static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->currentDrag(); if (!drag) { @@ -229,20 +269,18 @@ void QWaylandDataDevice::data_device_motion(uint32_t time, wl_fixed_t x, wl_fixe supportedActions = drag->supportedActions(); } else { dragData = m_dragOffer->mimeData(); - supportedActions = Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; + supportedActions = m_dragOffer->supportedActions(); } - QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_dragWindow, dragData, m_dragPoint, supportedActions); + const QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_dragWindow, dragData, m_dragPoint, supportedActions, + QGuiApplication::mouseButtons(), + m_inputDevice->modifiers()); if (drag) { static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->setResponse(response); } - if (response.isAccepted()) { - wl_data_offer_accept(m_dragOffer->object(), m_enterSerial, m_dragOffer->firstFormat().toUtf8().constData()); - } else { - wl_data_offer_accept(m_dragOffer->object(), m_enterSerial, nullptr); - } + sendResponse(supportedActions, response); } #endif // QT_CONFIG(draganddrop) @@ -269,12 +307,12 @@ void QWaylandDataDevice::selectionSourceCancelled() #if QT_CONFIG(draganddrop) void QWaylandDataDevice::dragSourceCancelled() { + static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag(); m_dragSource.reset(); -} - -void QWaylandDataDevice::dragSourceTargetChanged(const QString &mimeType) -{ - static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->updateTarget(mimeType); + if (m_toplevelDrag) { + m_toplevelDrag->destroy(); + m_toplevelDrag = nullptr; + } } QPoint QWaylandDataDevice::calculateDragPosition(int x, int y, QWindow *wnd) const @@ -289,8 +327,37 @@ QPoint QWaylandDataDevice::calculateDragPosition(int x, int y, QWindow *wnd) con } return pnt; } + +void QWaylandDataDevice::sendResponse(Qt::DropActions supportedActions, const QPlatformDragQtResponse &response) +{ + if (response.isAccepted()) { + if (version() >= 3) + m_dragOffer->set_actions(dropActionsToWl(supportedActions), dropActionsToWl(response.acceptedAction())); + + m_dragOffer->accept(m_enterSerial, m_dragOffer->firstFormat()); + } else { + m_dragOffer->accept(m_enterSerial, QString()); + } +} + +int QWaylandDataDevice::dropActionsToWl(Qt::DropActions actions) +{ + + int wlActions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + if (actions & Qt::CopyAction) + wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + if (actions & (Qt::MoveAction | Qt::TargetMoveAction)) + wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + + // wayland does not support LinkAction at the time of writing + return wlActions; +} + + #endif // QT_CONFIG(draganddrop) } QT_END_NAMESPACE + +#include "moc_qwaylanddatadevice_p.cpp" diff --git a/src/client/qwaylanddatadevice_p.h b/src/client/qwaylanddatadevice_p.h index 16c3ad28e..3dc4fcaf6 100644 --- a/src/client/qwaylanddatadevice_p.h +++ b/src/client/qwaylanddatadevice_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Klarälvdalens Datakonsult AB (KDAB). -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWaylandClient module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 Klarälvdalens Datakonsult AB (KDAB). +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDDATADEVICE_H @@ -64,8 +28,13 @@ QT_REQUIRE_CONFIG(wayland_datadevice); QT_BEGIN_NAMESPACE class QMimeData; +class QPlatformDragQtResponse; class QWindow; +namespace QtWayland { +class xdg_toplevel_drag_v1; +} + namespace QtWaylandClient { class QWaylandDisplay; @@ -89,7 +58,7 @@ public: #if QT_CONFIG(draganddrop) QWaylandDataOffer *dragOffer() const; - bool startDrag(QMimeData *mimeData, QWaylandWindow *icon); + bool startDrag(QMimeData *mimeData, Qt::DropActions supportedActions, QWaylandWindow *icon); void cancelDrag(); #endif @@ -109,13 +78,16 @@ private Q_SLOTS: #if QT_CONFIG(draganddrop) void dragSourceCancelled(); - void dragSourceTargetChanged(const QString &mimeType); #endif private: #if QT_CONFIG(draganddrop) QPoint calculateDragPosition(int x, int y, QWindow *wnd) const; #endif + void sendResponse(Qt::DropActions supportedActions, const QPlatformDragQtResponse &response); + + static int dropActionsToWl(Qt::DropActions dropActions); + QWaylandDisplay *m_display = nullptr; QWaylandInputDevice *m_inputDevice = nullptr; @@ -125,8 +97,8 @@ private: QScopedPointer<QWaylandDataOffer> m_dragOffer; QScopedPointer<QWaylandDataOffer> m_selectionOffer; QScopedPointer<QWaylandDataSource> m_selectionSource; - QScopedPointer<QWaylandDataSource> m_dragSource; + QtWayland::xdg_toplevel_drag_v1 *m_toplevelDrag = nullptr; }; } diff --git a/src/client/qwaylanddatadevicemanager.cpp b/src/client/qwaylanddatadevicemanager.cpp index 35d67307f..961d055ea 100644 --- a/src/client/qwaylanddatadevicemanager.cpp +++ b/src/client/qwaylanddatadevicemanager.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylanddatadevicemanager_p.h" @@ -50,8 +14,8 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { -QWaylandDataDeviceManager::QWaylandDataDeviceManager(QWaylandDisplay *display, uint32_t id) - : wl_data_device_manager(display->wl_registry(), id, 1) +QWaylandDataDeviceManager::QWaylandDataDeviceManager(QWaylandDisplay *display, int version, uint32_t id) + : wl_data_device_manager(display->wl_registry(), id, qMin(version, 3)) , m_display(display) { // Create transfer devices for all input devices. diff --git a/src/client/qwaylanddatadevicemanager_p.h b/src/client/qwaylanddatadevicemanager_p.h index bd05c0fbb..7e1cb1e45 100644 --- a/src/client/qwaylanddatadevicemanager_p.h +++ b/src/client/qwaylanddatadevicemanager_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDDATADEVICEMANAGER_H #define QWAYLANDDATADEVICEMANAGER_H @@ -65,10 +29,10 @@ class QWaylandDataDevice; class QWaylandDataSource; class QWaylandInputDevice; -class Q_WAYLAND_CLIENT_EXPORT QWaylandDataDeviceManager : public QtWayland::wl_data_device_manager +class Q_WAYLANDCLIENT_EXPORT QWaylandDataDeviceManager : public QtWayland::wl_data_device_manager { public: - QWaylandDataDeviceManager(QWaylandDisplay *display, uint32_t id); + QWaylandDataDeviceManager(QWaylandDisplay *display, int version, uint32_t id); ~QWaylandDataDeviceManager() override; QWaylandDataDevice *getDataDevice(QWaylandInputDevice *inputDevice); diff --git a/src/client/qwaylanddataoffer.cpp b/src/client/qwaylanddataoffer.cpp index 4c06277fe..8110ce35f 100644 --- a/src/client/qwaylanddataoffer.cpp +++ b/src/client/qwaylanddataoffer.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylanddataoffer_p.h" #include "qwaylanddatadevicemanager_p.h" @@ -47,6 +11,8 @@ #include <QtCore/QDebug> +using namespace std::chrono; + QT_BEGIN_NAMESPACE namespace QtWaylandClient { @@ -56,6 +22,65 @@ static QString utf8Text() return QStringLiteral("text/plain;charset=utf-8"); } +static QString uriList() +{ + return QStringLiteral("text/uri-list"); +} + +static QString mozUrl() +{ + return QStringLiteral("text/x-moz-url"); +} + +static QString portalFileTransfer() +{ + return QStringLiteral("application/vnd.portal.filetransfer"); +} + +static QByteArray convertData(const QString &originalMime, const QString &newMime, const QByteArray &data) +{ + if (originalMime == newMime) + return data; + + // Convert text/x-moz-url, which is an UTF-16 string of + // URL and page title pairs, all separated by line breaks, to text/uri-list. + // see also qtbase/src/plugins/platforms/xcb/qxcbmime.cpp + if (originalMime == uriList() && newMime == mozUrl()) { + if (data.size() > 1) { + const quint8 byte0 = data.at(0); + const quint8 byte1 = data.at(1); + + if ((byte0 == 0xff && byte1 == 0xfe) || (byte0 == 0xfe && byte1 == 0xff) + || (byte0 != 0 && byte1 == 0) || (byte0 == 0 && byte1 != 0)) { + QByteArray converted; + const QString str = QString::fromUtf16( + reinterpret_cast<const char16_t *>(data.constData()), data.size() / 2); + if (!str.isNull()) { + const auto urls = QStringView{str}.split(u'\n'); + // Only the URL is interesting, skip the page title. + for (int i = 0; i < urls.size(); i += 2) { + const QUrl url(urls.at(i).trimmed().toString()); + if (url.isValid()) { + converted += url.toEncoded(); + converted += "\r\n"; + } + } + } + return converted; + // 8 byte encoding, remove a possible 0 at the end. + } else { + QByteArray converted = data; + if (converted.endsWith('\0')) + converted.chop(1); + converted += "\r\n"; + return converted; + } + } + } + + return data; +} + QWaylandDataOffer::QWaylandDataOffer(QWaylandDisplay *display, struct ::wl_data_offer *offer) : QtWayland::wl_data_offer(offer) , m_display(display) @@ -82,6 +107,15 @@ QMimeData *QWaylandDataOffer::mimeData() return m_mimeData.data(); } +Qt::DropActions QWaylandDataOffer::supportedActions() const +{ + if (version() < 3) { + return Qt::MoveAction | Qt::CopyAction; + } + + return m_supportedActions; +} + void QWaylandDataOffer::startReceiving(const QString &mimeType, int fd) { receive(mimeType, fd); @@ -93,6 +127,22 @@ void QWaylandDataOffer::data_offer_offer(const QString &mime_type) m_mimeData->appendFormat(mime_type); } +void QWaylandDataOffer::data_offer_action(uint32_t dnd_action) +{ + Q_UNUSED(dnd_action); + // This is the compositor telling the drag target what action it should perform + // It does not map nicely into Qt final drop semantics, other than pretending there is only one supported action? +} + +void QWaylandDataOffer::data_offer_source_actions(uint32_t source_actions) +{ + m_supportedActions = Qt::DropActions(); + if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) + m_supportedActions |= Qt::MoveAction; + if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) + m_supportedActions |= Qt::CopyAction; +} + QWaylandMimeData::QWaylandMimeData(QWaylandAbstractDataOffer *dataOffer) : m_dataOffer(dataOffer) { @@ -104,8 +154,11 @@ QWaylandMimeData::~QWaylandMimeData() void QWaylandMimeData::appendFormat(const QString &mimeType) { - m_types << mimeType; - m_data.remove(mimeType); // Clear previous contents + // "DELETE" is a potential leftover from XdndActionMode sent by e.g. Firefox, ignore it. + if (mimeType != QLatin1String("DELETE")) { + m_types << mimeType; + m_data.remove(mimeType); // Clear previous contents + } } bool QWaylandMimeData::hasFormat_sys(const QString &mimeType) const @@ -116,6 +169,9 @@ bool QWaylandMimeData::hasFormat_sys(const QString &mimeType) const if (mimeType == QStringLiteral("text/plain") && m_types.contains(utf8Text())) return true; + if (mimeType == uriList() && m_types.contains(mozUrl())) + return true; + return false; } @@ -124,18 +180,21 @@ QStringList QWaylandMimeData::formats_sys() const return m_types; } -QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QVariant::Type type) const +QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QMetaType type) const { Q_UNUSED(type); - if (m_data.contains(mimeType)) - return m_data.value(mimeType); + auto it = m_data.constFind(mimeType); + if (it != m_data.constEnd()) + return *it; QString mime = mimeType; if (!m_types.contains(mimeType)) { if (mimeType == QStringLiteral("text/plain") && m_types.contains(utf8Text())) mime = utf8Text(); + else if (mimeType == uriList() && m_types.contains(mozUrl())) + mime = mozUrl(); else return QVariant(); } @@ -157,37 +216,42 @@ QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QVariant::T } close(pipefd[0]); - m_data.insert(mimeType, content); + + content = convertData(mimeType, mime, content); + + if (mimeType != portalFileTransfer()) + m_data.insert(mimeType, content); + return content; } int QWaylandMimeData::readData(int fd, QByteArray &data) const { - fd_set readset; - FD_ZERO(&readset); - FD_SET(fd, &readset); - struct timeval timeout; - timeout.tv_sec = 1; - timeout.tv_usec = 0; - - int ready = select(FD_SETSIZE, &readset, nullptr, nullptr, &timeout); - if (ready < 0) { - qWarning() << "QWaylandDataOffer: select() failed"; - return -1; - } else if (ready == 0) { - qWarning("QWaylandDataOffer: timeout reading from pipe"); - return -1; - } else { - char buf[4096]; - int n = QT_READ(fd, buf, sizeof buf); - - if (n > 0) { - data.append(buf, n); - n = readData(fd, data); - } else if (n < 0) { - qWarning("QWaylandDataOffer: read() failed"); + struct pollfd readset; + readset.fd = fd; + readset.events = POLLIN; + + Q_FOREVER { + int ready = qt_safe_poll(&readset, 1, QDeadlineTimer(1s)); + if (ready < 0) { + qWarning() << "QWaylandDataOffer: qt_safe_poll() failed"; + return -1; + } else if (ready == 0) { + qWarning("QWaylandDataOffer: timeout reading from pipe"); + return -1; + } else { + char buf[4096]; + int n = QT_READ(fd, buf, sizeof buf); + + if (n < 0) { + qWarning("QWaylandDataOffer: read() failed"); + return -1; + } else if (n == 0) { + return 0; + } else if (n > 0) { + data.append(buf, n); + } } - return n; } } diff --git a/src/client/qwaylanddataoffer_p.h b/src/client/qwaylanddataoffer_p.h index 9cf1483ca..1c99147d2 100644 --- a/src/client/qwaylanddataoffer_p.h +++ b/src/client/qwaylanddataoffer_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDDATAOFFER_H #define QWAYLANDDATAOFFER_H @@ -51,6 +15,9 @@ // We mean it. // +#include <QtCore/qhash.h> +#include <QtCore/qstring.h> + #include <QtGui/private/qinternalmimedata_p.h> #include <QtWaylandClient/private/qtwaylandclientglobal_p.h> @@ -74,7 +41,7 @@ public: virtual ~QWaylandAbstractDataOffer() = default; }; -class Q_WAYLAND_CLIENT_EXPORT QWaylandDataOffer +class Q_WAYLANDCLIENT_EXPORT QWaylandDataOffer : public QtWayland::wl_data_offer // needs to be the first because we do static casts from the user pointer to the wrapper , public QWaylandAbstractDataOffer { @@ -82,6 +49,7 @@ public: explicit QWaylandDataOffer(QWaylandDisplay *display, struct ::wl_data_offer *offer); ~QWaylandDataOffer() override; QMimeData *mimeData() override; + Qt::DropActions supportedActions() const; QString firstFormat() const; @@ -89,10 +57,13 @@ public: protected: void data_offer_offer(const QString &mime_type) override; + void data_offer_source_actions(uint32_t source_actions) override; + void data_offer_action(uint32_t dnd_action) override; private: QWaylandDisplay *m_display = nullptr; QScopedPointer<QWaylandMimeData> m_mimeData; + Qt::DropActions m_supportedActions; }; @@ -106,7 +77,7 @@ public: protected: bool hasFormat_sys(const QString &mimeType) const override; QStringList formats_sys() const override; - QVariant retrieveData_sys(const QString &mimeType, QVariant::Type type) const override; + QVariant retrieveData_sys(const QString &mimeType, QMetaType type) const override; private: int readData(int fd, QByteArray &data) const; diff --git a/src/client/qwaylanddatasource.cpp b/src/client/qwaylanddatasource.cpp index ea76943a7..966d5ef7f 100644 --- a/src/client/qwaylanddatasource.cpp +++ b/src/client/qwaylanddatasource.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylanddatasource_p.h" #include "qwaylanddataoffer_p.h" @@ -49,6 +13,7 @@ #include <unistd.h> #include <signal.h> +#include <fcntl.h> QT_BEGIN_NAMESPACE @@ -60,7 +25,7 @@ QWaylandDataSource::QWaylandDataSource(QWaylandDataDeviceManager *dataDeviceMana { if (!mimeData) return; - const auto formats = mimeData->formats(); + const auto formats = QInternalMimeData::formatsHelper(mimeData); for (const QString &format : formats) { offer(format); } @@ -71,11 +36,6 @@ QWaylandDataSource::~QWaylandDataSource() destroy(); } -QMimeData * QWaylandDataSource::mimeData() const -{ - return m_mime_data; -} - void QWaylandDataSource::data_source_cancelled() { Q_EMIT cancelled(); @@ -93,7 +53,15 @@ void QWaylandDataSource::data_source_send(const QString &mime_type, int32_t fd) action.sa_flags = 0; sigaction(SIGPIPE, &action, &oldAction); - write(fd, content.constData(), content.size()); + // Some compositors (e.g., mutter) make fd with O_NONBLOCK. + // Since wl_data_source.send describes that fd is closed here, + // it should be done in a loop and don't have any advantage. + // Blocking operation will be used. + // According to fcntl(2), FSETFL ignores O_WRONLY. So this + // call will just remove O_NONBLOCK. + fcntl(fd, F_SETFL, O_WRONLY); + ssize_t unused = write(fd, content.constData(), content.size()); + Q_UNUSED(unused); sigaction(SIGPIPE, &oldAction, nullptr); } close(fd); @@ -101,9 +69,36 @@ void QWaylandDataSource::data_source_send(const QString &mime_type, int32_t fd) void QWaylandDataSource::data_source_target(const QString &mime_type) { - Q_EMIT targetChanged(mime_type); + m_accepted = !mime_type.isEmpty(); + Q_EMIT dndResponseUpdated(m_accepted, m_dropAction); +} + +void QWaylandDataSource::data_source_action(uint32_t action) +{ + Qt::DropAction qtAction = Qt::IgnoreAction; + + if (action == WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) + qtAction = Qt::MoveAction; + else if (action == WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) + qtAction = Qt::CopyAction; + + m_dropAction = qtAction; + Q_EMIT dndResponseUpdated(m_accepted, m_dropAction); +} + +void QWaylandDataSource::data_source_dnd_finished() +{ + Q_EMIT finished(); +} + +void QWaylandDataSource::data_source_dnd_drop_performed() +{ + + Q_EMIT dndDropped(m_accepted, m_dropAction); } } QT_END_NAMESPACE + +#include "moc_qwaylanddatasource_p.cpp" diff --git a/src/client/qwaylanddatasource_p.h b/src/client/qwaylanddatasource_p.h index 25afff79c..a4b317c26 100644 --- a/src/client/qwaylanddatasource_p.h +++ b/src/client/qwaylanddatasource_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDDATASOURCE_H #define QWAYLANDDATASOURCE_H @@ -67,27 +31,32 @@ namespace QtWaylandClient { class QWaylandDataDeviceManager; class QWaylandDisplay; -class Q_WAYLAND_CLIENT_EXPORT QWaylandDataSource : public QObject, public QtWayland::wl_data_source +class Q_WAYLANDCLIENT_EXPORT QWaylandDataSource : public QObject, public QtWayland::wl_data_source { Q_OBJECT public: QWaylandDataSource(QWaylandDataDeviceManager *dataDeviceManager, QMimeData *mimeData); ~QWaylandDataSource() override; - QMimeData *mimeData() const; - Q_SIGNALS: - void targetChanged(const QString &mime_type); void cancelled(); + void finished(); + + void dndResponseUpdated(bool accepted, Qt::DropAction action); + void dndDropped(bool accepted, Qt::DropAction action); protected: void data_source_cancelled() override; void data_source_send(const QString &mime_type, int32_t fd) override; void data_source_target(const QString &mime_type) override; + void data_source_dnd_drop_performed() override; + void data_source_dnd_finished() override; + void data_source_action(uint32_t action) override; private: - QWaylandDisplay *m_display = nullptr; QMimeData *m_mime_data = nullptr; + bool m_accepted = false; + Qt::DropAction m_dropAction = Qt::IgnoreAction; }; } diff --git a/src/client/qwaylanddecorationfactory.cpp b/src/client/qwaylanddecorationfactory.cpp index 32db96e58..b716a4aee 100644 --- a/src/client/qwaylanddecorationfactory.cpp +++ b/src/client/qwaylanddecorationfactory.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylanddecorationfactory_p.h" #include "qwaylanddecorationplugin_p.h" @@ -48,50 +12,17 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { -#if QT_CONFIG(library) -Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, qwdfiLoader, (QWaylandDecorationFactoryInterface_iid, QLatin1String("/wayland-decoration-client"), Qt::CaseInsensitive)) -Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, directLoader, - (QWaylandDecorationFactoryInterface_iid, QLatin1String(""), Qt::CaseInsensitive)) -#endif -QStringList QWaylandDecorationFactory::keys(const QString &pluginPath) +QStringList QWaylandDecorationFactory::keys() { -#if QT_CONFIG(library) - QStringList list; - if (!pluginPath.isEmpty()) { - QCoreApplication::addLibraryPath(pluginPath); - list = directLoader()->keyMap().values(); - if (!list.isEmpty()) { - const QString postFix = QStringLiteral(" (from ") - + QDir::toNativeSeparators(pluginPath) - + QLatin1Char(')'); - const QStringList::iterator end = list.end(); - for (QStringList::iterator it = list.begin(); it != end; ++it) - (*it).append(postFix); - } - } - list.append(loader()->keyMap().values()); - return list; -#else - return QStringList(); -#endif + return qwdfiLoader->keyMap().values(); } -QWaylandAbstractDecoration *QWaylandDecorationFactory::create(const QString &name, const QStringList &args, const QString &pluginPath) +QWaylandAbstractDecoration *QWaylandDecorationFactory::create(const QString &name, const QStringList &args) { -#if QT_CONFIG(library) - // Try loading the plugin from platformPluginPath first: - if (!pluginPath.isEmpty()) { - QCoreApplication::addLibraryPath(pluginPath); - if (QWaylandAbstractDecoration *ret = qLoadPlugin<QWaylandAbstractDecoration, QWaylandDecorationPlugin>(directLoader(), name, args)) - return ret; - } - if (QWaylandAbstractDecoration *ret = qLoadPlugin<QWaylandAbstractDecoration, QWaylandDecorationPlugin>(loader(), name, args)) - return ret; -#endif - - return nullptr; + return qLoadPlugin<QWaylandAbstractDecoration, QWaylandDecorationPlugin>(qwdfiLoader(), name, args); } } diff --git a/src/client/qwaylanddecorationfactory_p.h b/src/client/qwaylanddecorationfactory_p.h index 606d9b89c..bd0998631 100644 --- a/src/client/qwaylanddecorationfactory_p.h +++ b/src/client/qwaylanddecorationfactory_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDDECORATIONFACTORY_H #define QWAYLANDDECORATIONFACTORY_H @@ -53,6 +17,7 @@ #include <QtWaylandClient/qtwaylandclientglobal.h> #include <QtCore/QStringList> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -60,11 +25,11 @@ namespace QtWaylandClient { class QWaylandAbstractDecoration; -class Q_WAYLAND_CLIENT_EXPORT QWaylandDecorationFactory +class Q_WAYLANDCLIENT_EXPORT QWaylandDecorationFactory { public: - static QStringList keys(const QString &pluginPath = QString()); - static QWaylandAbstractDecoration *create(const QString &name, const QStringList &args, const QString &pluginPath = QString()); + static QStringList keys(); + static QWaylandAbstractDecoration *create(const QString &name, const QStringList &args); }; } diff --git a/src/client/qwaylanddecorationplugin.cpp b/src/client/qwaylanddecorationplugin.cpp index 15d907de9..f64211996 100644 --- a/src/client/qwaylanddecorationplugin.cpp +++ b/src/client/qwaylanddecorationplugin.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylanddecorationplugin_p.h" @@ -54,3 +18,5 @@ QWaylandDecorationPlugin::~QWaylandDecorationPlugin() } QT_END_NAMESPACE + +#include "moc_qwaylanddecorationplugin_p.cpp" diff --git a/src/client/qwaylanddecorationplugin_p.h b/src/client/qwaylanddecorationplugin_p.h index 41daaea05..e09d99702 100644 --- a/src/client/qwaylanddecorationplugin_p.h +++ b/src/client/qwaylanddecorationplugin_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDDECORATIONPLUGIN_H #define QWAYLANDDECORATIONPLUGIN_H @@ -56,6 +20,7 @@ #include <QtCore/qplugin.h> #include <QtCore/qfactoryinterface.h> #include <QtCore/QObject> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -65,7 +30,7 @@ class QWaylandAbstractDecoration; #define QWaylandDecorationFactoryInterface_iid "org.qt-project.Qt.WaylandClient.QWaylandDecorationFactoryInterface.5.4" -class Q_WAYLAND_CLIENT_EXPORT QWaylandDecorationPlugin : public QObject +class Q_WAYLANDCLIENT_EXPORT QWaylandDecorationPlugin : public QObject { Q_OBJECT public: diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp index 0464d3a42..f78bf2dd2 100644 --- a/src/client/qwaylanddisplay.cpp +++ b/src/client/qwaylanddisplay.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylanddisplay_p.h" @@ -60,19 +24,33 @@ #include <wayland-cursor.h> #endif #include "qwaylandhardwareintegration_p.h" +#include "qwaylandtextinputv1_p.h" +#include "qwaylandtextinputv2_p.h" +#include "qwaylandtextinputv3_p.h" #include "qwaylandinputcontext_p.h" +#include "qwaylandinputmethodcontext_p.h" #include "qwaylandwindowmanagerintegration_p.h" #include "qwaylandshellintegration_p.h" #include "qwaylandclientbufferintegration_p.h" -#include "qwaylandextendedsurface_p.h" +#include "qwaylandpointergestures_p.h" #include "qwaylandsubsurface_p.h" #include "qwaylandtouch_p.h" +#if QT_CONFIG(tabletevent) +#include "qwaylandtabletv2_p.h" +#endif #include "qwaylandqtkey_p.h" +#include <QtWaylandClient/private/qwayland-text-input-unstable-v1.h> #include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h> +#include <QtWaylandClient/private/qwayland-text-input-unstable-v3.h> #include <QtWaylandClient/private/qwayland-wp-primary-selection-unstable-v1.h> +#include <QtWaylandClient/private/qwayland-qt-text-input-method-unstable-v1.h> +#include <QtWaylandClient/private/qwayland-fractional-scale-v1.h> +#include <QtWaylandClient/private/qwayland-viewporter.h> +#include <QtWaylandClient/private/qwayland-cursor-shape-v1.h> +#include <QtWaylandClient/private/qwayland-xdg-toplevel-drag-v1.h> #include <QtCore/private/qcore_unix_p.h> @@ -84,22 +62,211 @@ #include <errno.h> +#include <tuple> // for std::tie + QT_BEGIN_NAMESPACE namespace QtWaylandClient { +class EventThread : public QThread +{ + Q_OBJECT +public: + enum OperatingMode { + EmitToDispatch, // Emit the signal, allow dispatching in a differnt thread. + SelfDispatch, // Dispatch the events inside this thread. + }; + + EventThread(struct wl_display * wl, struct wl_event_queue * ev_queue, + OperatingMode mode) + : m_fd(wl_display_get_fd(wl)) + , m_pipefd{ -1, -1 } + , m_wldisplay(wl) + , m_wlevqueue(ev_queue) + , m_mode(mode) + , m_reading(true) + , m_quitting(false) + { + setObjectName(QStringLiteral("WaylandEventThread")); + } + + void readAndDispatchEvents() + { + /* + * Dispatch pending events and flush the requests at least once. If the event thread + * is not reading, try to call _prepare_read() to allow the event thread to poll(). + * If that fails, re-try dispatch & flush again until _prepare_read() is successful. + * + * This allow any call to readAndDispatchEvents() to start event thread's polling, + * not only the one issued from event thread's waitForReading(), which means functions + * called from dispatch_pending() can safely spin an event loop. + */ + if (m_quitting) + return; + + for (;;) { + if (dispatchQueuePending() < 0) { + Q_EMIT waylandError(); + m_quitting = true; + return; + } + + wl_display_flush(m_wldisplay); + + // We have to check if event thread is reading every time we dispatch + // something, as that may recursively call this function. + if (m_reading.loadAcquire()) + break; + + if (prepareReadQueue() == 0) { + QMutexLocker l(&m_mutex); + m_reading.storeRelease(true); + m_cond.wakeOne(); + break; + } + } + } + + void stop() + { + // We have to both write to the pipe and set the flag, as the thread may be + // either in the poll() or waiting for _prepare_read(). + if (m_pipefd[1] != -1 && write(m_pipefd[1], "\0", 1) == -1) + qWarning("Failed to write to the pipe: %s.", strerror(errno)); + + { + QMutexLocker l(&m_mutex); + m_quitting = true; + m_cond.wakeOne(); + } + + wait(); + } + +Q_SIGNALS: + void needReadAndDispatch(); + void waylandError(); + +protected: + void run() override + { + // we use this pipe to make the loop exit otherwise if we simply used a flag on the loop condition, if stop() gets + // called while poll() is blocking the thread will never quit since there are no wayland messages coming anymore. + struct Pipe + { + Pipe(int *fds) + : fds(fds) + { + if (qt_safe_pipe(fds) != 0) + qWarning("Pipe creation failed. Quitting may hang."); + } + ~Pipe() + { + if (fds[0] != -1) { + close(fds[0]); + close(fds[1]); + } + } + + int *fds; + } pipe(m_pipefd); + + // Make the main thread call wl_prepare_read(), dispatch the pending messages and flush the + // outbound ones. Wait until it's done before proceeding, unless we're told to quit. + while (waitForReading()) { + if (!m_reading.loadRelaxed()) + break; + + pollfd fds[2] = { { m_fd, POLLIN, 0 }, { m_pipefd[0], POLLIN, 0 } }; + poll(fds, 2, -1); + + if (fds[1].revents & POLLIN) { + // we don't really care to read the byte that was written here since we're closing down + wl_display_cancel_read(m_wldisplay); + break; + } + + if (fds[0].revents & POLLIN) + wl_display_read_events(m_wldisplay); + // The polll was succesfull and the event thread did the wl_display_read_events(). On the next iteration of the loop + // the event sent to the main thread will cause it to dispatch the messages just read, unless the loop exits in which + // case we don't care anymore about them. + else + wl_display_cancel_read(m_wldisplay); + } + } + +private: + bool waitForReading() + { + Q_ASSERT(QThread::currentThread() == this); + + m_reading.storeRelease(false); + + if (m_mode == SelfDispatch) { + readAndDispatchEvents(); + } else { + Q_EMIT needReadAndDispatch(); + + QMutexLocker lock(&m_mutex); + // m_reading might be set from our emit or some other invocation of + // readAndDispatchEvents(). + while (!m_reading.loadRelaxed() && !m_quitting) + m_cond.wait(&m_mutex); + } + + return !m_quitting; + } + + int dispatchQueuePending() + { + if (m_wlevqueue) + return wl_display_dispatch_queue_pending(m_wldisplay, m_wlevqueue); + else + return wl_display_dispatch_pending(m_wldisplay); + } + + int prepareReadQueue() + { + if (m_wlevqueue) + return wl_display_prepare_read_queue(m_wldisplay, m_wlevqueue); + else + return wl_display_prepare_read(m_wldisplay); + } + + int m_fd; + int m_pipefd[2]; + wl_display *m_wldisplay; + wl_event_queue *m_wlevqueue; + OperatingMode m_mode; + + /* Concurrency note when operating in EmitToDispatch mode: + * m_reading is set to false inside event thread's waitForReading(), and is + * set to true inside main thread's readAndDispatchEvents(). + * The lock is not taken when setting m_reading to false, as the main thread + * is not actively waiting for it to turn false. However, the lock is taken + * inside readAndDispatchEvents() before setting m_reading to true, + * as the event thread is actively waiting for it under the wait condition. + */ + + QAtomicInteger<bool> m_reading; + bool m_quitting; + QMutex m_mutex; + QWaitCondition m_cond; +}; + Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland"); // for general (uncategorized) Wayland platform logging struct wl_surface *QWaylandDisplay::createSurface(void *handle) { - struct wl_surface *surface = mCompositor.create_surface(); + struct wl_surface *surface = mGlobals.compositor->create_surface(); wl_surface_set_user_data(surface, handle); return surface; } struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion) { - struct ::wl_region *region = mCompositor.create_region(); + struct ::wl_region *region = mGlobals.compositor->create_region(); for (const QRect &rect : qregion) wl_region_add(region, rect.x(), rect.y(), rect.width(), rect.height()); @@ -109,7 +276,7 @@ struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion) ::wl_subsurface *QWaylandDisplay::createSubSurface(QWaylandWindow *window, QWaylandWindow *parent) { - if (!mSubCompositor) { + if (!mGlobals.subCompositor) { qCWarning(lcQpaWayland) << "Can't create subsurface, not supported by the compositor."; return nullptr; } @@ -118,7 +285,18 @@ struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion) Q_ASSERT(parent->wlSurface()); Q_ASSERT(window->wlSurface()); - return mSubCompositor->get_subsurface(window->wlSurface(), parent->wlSurface()); + return mGlobals.subCompositor->get_subsurface(window->wlSurface(), parent->wlSurface()); +} + +::wp_viewport *QWaylandDisplay::createViewport(QWaylandWindow *window) +{ + if (!mGlobals.viewporter) { + qCWarning(lcQpaWayland) << "Can't create wp_viewport, not supported by the compositor."; + return nullptr; + } + + Q_ASSERT(window->wlSurface()); + return mGlobals.viewporter->get_viewport(window->wlSurface()); } QWaylandShellIntegration *QWaylandDisplay::shellIntegration() const @@ -133,7 +311,7 @@ QWaylandClientBufferIntegration * QWaylandDisplay::clientBufferIntegration() con QWaylandWindowManagerIntegration *QWaylandDisplay::windowManagerIntegration() const { - return mWindowManagerIntegration.data(); + return mGlobals.windowManagerIntegration.get(); } QWaylandDisplay::QWaylandDisplay(QWaylandIntegration *waylandIntegration) @@ -142,119 +320,274 @@ QWaylandDisplay::QWaylandDisplay(QWaylandIntegration *waylandIntegration) qRegisterMetaType<uint32_t>("uint32_t"); mDisplay = wl_display_connect(nullptr); - if (!mDisplay) { + if (mDisplay) { + setupConnection(); + } else { qErrnoWarning(errno, "Failed to create wl_display"); - return; } + mWaylandTryReconnect = qEnvironmentVariableIsSet("QT_WAYLAND_RECONNECT"); +} + +void QWaylandDisplay::setupConnection() +{ struct ::wl_registry *registry = wl_display_get_registry(mDisplay); init(registry); - mWindowManagerIntegration.reset(new QWaylandWindowManagerIntegration(this)); - #if QT_CONFIG(xkbcommon) mXkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS)); if (!mXkbContext) qCWarning(lcQpaWayland, "failed to create xkb context"); #endif - - forceRoundTrip(); - - if (!mWaitingScreens.isEmpty()) { - // Give wl_output.done and zxdg_output_v1.done events a chance to arrive - forceRoundTrip(); - } + if (mWaylandInputContextRequested) + checkTextInputProtocol(); } QWaylandDisplay::~QWaylandDisplay(void) { + if (m_eventThread) + m_eventThread->stop(); + + if (m_frameEventQueueThread) + m_frameEventQueueThread->stop(); + if (mSyncCallback) wl_callback_destroy(mSyncCallback); - qDeleteAll(qExchange(mInputDevices, {})); + qDeleteAll(std::exchange(mInputDevices, {})); - for (QWaylandScreen *screen : qExchange(mScreens, {})) { + for (QWaylandScreen *screen : std::exchange(mScreens, {})) { QWindowSystemInterface::handleScreenRemoved(screen); } qDeleteAll(mWaitingScreens); -#if QT_CONFIG(wayland_datadevice) - delete mDndSelectionHandler.take(); -#endif #if QT_CONFIG(cursor) - qDeleteAll(mCursorThemes); + mCursorThemes.clear(); #endif + + if (m_frameEventQueue) + wl_event_queue_destroy(m_frameEventQueue); + + // Reset the globals manually since they need to be destroyed before the wl_display + mGlobals = {}; + + if (object()) + wl_registry_destroy(object()); + if (mDisplay) wl_display_disconnect(mDisplay); } -void QWaylandDisplay::checkError() const +// Steps which is called just after constructor. This separates registry_global() out of the constructor +// so that factory functions in integration can be overridden. +bool QWaylandDisplay::initialize() { - int ecode = wl_display_get_error(mDisplay); - if ((ecode == EPIPE || ecode == ECONNRESET)) { - // special case this to provide a nicer error - qFatal("The Wayland connection broke. Did the Wayland compositor die?"); - } else { - qFatal("The Wayland connection experienced a fatal error: %s", strerror(ecode)); + if (!isInitialized()) + return false; + + forceRoundTrip(); + + emit connected(); + + if (!mWaitingScreens.isEmpty()) { + // Give wl_output.done and zxdg_output_v1.done events a chance to arrive + forceRoundTrip(); } + if (mWaylandInputContextRequested) + mTextInputManagerIndex = INT_MAX; + + return qEnvironmentVariableIntValue("QT_WAYLAND_DONT_CHECK_SHELL_INTEGRATION") || shellIntegration(); } -void QWaylandDisplay::flushRequests() +void QWaylandDisplay::ensureScreen() { - if (wl_display_prepare_read(mDisplay) == 0) { - wl_display_read_events(mDisplay); - } + if (!mScreens.empty() || mPlaceholderScreen) + return; // There are real screens or we already have a fake one - if (wl_display_dispatch_pending(mDisplay) < 0) - checkError(); + qCInfo(lcQpaWayland) << "Creating a fake screen in order for Qt not to crash"; - wl_display_flush(mDisplay); + mPlaceholderScreen = new QPlatformPlaceholderScreen(); + QWindowSystemInterface::handleScreenAdded(mPlaceholderScreen); + Q_ASSERT(!QGuiApplication::screens().empty()); } -void QWaylandDisplay::blockingReadEvents() +void QWaylandDisplay::reconnect() { - if (wl_display_dispatch(mDisplay) < 0) - checkError(); + qCWarning(lcQpaWayland) << "Attempting wayland reconnect"; + m_eventThread->stop(); + m_frameEventQueueThread->stop(); + m_eventThread->wait(); + m_frameEventQueueThread->wait(); + + qDeleteAll(mWaitingScreens); + mWaitingScreens.clear(); + + while (!mScreens.isEmpty()) { + auto screen = mScreens.takeLast(); + ensureScreen(); + QWindowSystemInterface::handleScreenRemoved(screen); + } + + mCursorThemes.clear(); + mCursor.reset(); + + mGlobals = GlobalHolder(); + + mWaylandIntegration->reset(); + + qDeleteAll(std::exchange(mInputDevices, {})); + mLastInputDevice = nullptr; + + for (const RegistryGlobal &global : mRegistryGlobals) { + emit globalRemoved(global); + } + mRegistryGlobals.clear(); + + mLastInputSerial = 0; + mLastInputWindow.clear(); + mLastKeyboardFocus.clear(); + mActiveWindows.clear(); + + const auto windows = QGuiApplication::allWindows(); + for (auto window : windows) { + if (auto waylandWindow = static_cast<QWaylandWindow *>(window->handle())) + waylandWindow->closeChildPopups(); + } + // Remove windows that do not need to be recreated and now closed popups + QList<QWaylandWindow *> recreateWindows; + for (auto window : std::as_const(windows)) { + auto waylandWindow = static_cast<QWaylandWindow*>(window->handle()); + if (waylandWindow && waylandWindow->wlSurface()) { + waylandWindow->reset(); + recreateWindows.push_back(waylandWindow); + } + } + + if (mSyncCallback) { + wl_callback_destroy(mSyncCallback); + mSyncCallback = nullptr; + } + + if (object()) + wl_registry_destroy(object()); + mDisplay = wl_display_connect(nullptr); + if (!mDisplay) + _exit(1); + + setupConnection(); + initialize(); + + if (m_frameEventQueue) + wl_event_queue_destroy(m_frameEventQueue); + initEventThread(); + + auto needsRecreate = [](QPlatformWindow *window) { + return window && !static_cast<QWaylandWindow *>(window)->wlSurface(); + }; + auto window = recreateWindows.begin(); + while (!recreateWindows.isEmpty()) { + if (!needsRecreate((*window)->QPlatformWindow::parent()) && !needsRecreate((*window)->transientParent())) { + (*window)->reinit(); + window = recreateWindows.erase(window); + } else { + ++window; + } + if (window == recreateWindows.end()) + window = recreateWindows.begin(); + } + + mWaylandIntegration->reconfigureInputContext(); } -wl_event_queue *QWaylandDisplay::createEventQueue() +void QWaylandDisplay::flushRequests() { - return wl_display_create_queue(mDisplay); + m_eventThread->readAndDispatchEvents(); } -void QWaylandDisplay::dispatchQueueWhile(wl_event_queue *queue, std::function<bool ()> condition, int timeout) +// We have to wait until we have an eventDispatcher before creating the eventThread, +// otherwise forceRoundTrip() may block inside _events_read() because eventThread is +// polling. +void QWaylandDisplay::initEventThread() { - if (!condition()) - return; - - QElapsedTimer timer; - timer.start(); - struct pollfd pFd = qt_make_pollfd(wl_display_get_fd(mDisplay), POLLIN); - while (timeout == -1 || timer.elapsed() < timeout) { - while (wl_display_prepare_read_queue(mDisplay, queue) != 0) - wl_display_dispatch_queue_pending(mDisplay, queue); + m_eventThread.reset( + new EventThread(mDisplay, /* default queue */ nullptr, EventThread::EmitToDispatch)); + connect(m_eventThread.get(), &EventThread::needReadAndDispatch, this, + &QWaylandDisplay::flushRequests, Qt::QueuedConnection); + connect(m_eventThread.get(), &EventThread::waylandError, this, + &QWaylandDisplay::checkWaylandError, Qt::QueuedConnection); + m_eventThread->start(); + + // wl_display_disconnect() free this. + m_frameEventQueue = wl_display_create_queue(mDisplay); + m_frameEventQueueThread.reset( + new EventThread(mDisplay, m_frameEventQueue, EventThread::SelfDispatch)); + m_frameEventQueueThread->start(); +} - wl_display_flush(mDisplay); +void QWaylandDisplay::checkWaylandError() +{ + int ecode = wl_display_get_error(mDisplay); + if ((ecode == EPIPE || ecode == ECONNRESET)) { + qWarning("The Wayland connection broke. Did the Wayland compositor die?"); + if (mWaylandTryReconnect) { + reconnect(); + return; + } + } else { + qWarning("The Wayland connection experienced a fatal error: %s", strerror(ecode)); + } + _exit(-1); +} - const int remaining = qMax(timeout - timer.elapsed(), 0ll); - const int pollTimeout = timeout == -1 ? -1 : remaining; - if (qt_poll_msecs(&pFd, 1, pollTimeout) > 0) - wl_display_read_events(mDisplay); +void QWaylandDisplay::blockingReadEvents() +{ + if (wl_display_dispatch(mDisplay) < 0) { + int ecode = wl_display_get_error(mDisplay); + if ((ecode == EPIPE || ecode == ECONNRESET)) + qWarning("The Wayland connection broke during blocking read event. Did the Wayland compositor die?"); else - wl_display_cancel_read(mDisplay); - - if (wl_display_dispatch_queue_pending(mDisplay, queue) < 0) - checkError(); + qWarning("The Wayland connection experienced a fatal error during blocking read event: %s", strerror(ecode)); + _exit(-1); + } +} - if (!condition()) - break; +void QWaylandDisplay::checkTextInputProtocol() +{ + QStringList tips, timps; // for text input protocols and text input manager protocols + // zwp_text_input_v2 is preferred over zwp_text_input_v3 because: + // - Currently, v3 is not as feature rich as v2. + // - While v2 is not upstreamed, it is well supported by KWin since Plasma 5 and Plasma + // Mobile uses some v2 only. + tips << QLatin1String(QtWayland::qt_text_input_method_v1::interface()->name) + << QLatin1String(QtWayland::zwp_text_input_v2::interface()->name) + << QLatin1String(QtWayland::zwp_text_input_v3::interface()->name) + << QLatin1String(QtWayland::zwp_text_input_v1::interface()->name); + timps << QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name) + << QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name) + << QLatin1String(QtWayland::zwp_text_input_manager_v3::interface()->name) + << QLatin1String(QtWayland::zwp_text_input_manager_v1::interface()->name); + + QString tiProtocols = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_TEXT_INPUT_PROTOCOL")); + qCDebug(lcQpaWayland) << "QT_WAYLAND_TEXT_INPUT_PROTOCOL=" << tiProtocols; + QStringList keys; + if (!tiProtocols.isEmpty()) { + keys = tiProtocols.split(QLatin1Char(';')); + QList<QString>::iterator it = keys.begin(); + while (it != keys.end()) { + if (tips.contains(*it)) + mTextInputManagerList.append(timps.at(tips.indexOf(*it))); + else + qCDebug(lcQpaWayland) << "text input: unknown protocol - " << *it; + ++it; + } } + if (mTextInputManagerList.isEmpty()) // fallback + mTextInputManagerList = timps; } QWaylandScreen *QWaylandDisplay::screenForOutput(struct wl_output *output) const { - for (int i = 0; i < mScreens.size(); ++i) { - QWaylandScreen *screen = static_cast<QWaylandScreen *>(mScreens.at(i)); + for (auto screen : std::as_const(mScreens)) { if (screen->output() == output) return screen; } @@ -267,78 +600,186 @@ void QWaylandDisplay::handleScreenInitialized(QWaylandScreen *screen) return; mScreens.append(screen); QWindowSystemInterface::handleScreenAdded(screen); + if (mPlaceholderScreen) { + QWindowSystemInterface::handleScreenRemoved(mPlaceholderScreen); + // handleScreenRemoved deletes the platform screen + mPlaceholderScreen = nullptr; + } } -void QWaylandDisplay::waitForScreens() +template <typename T, auto f> +struct WithDestructor : public T { - flushRequests(); - - while (true) { - bool screensReady = !mScreens.isEmpty(); - - for (int ii = 0; screensReady && ii < mScreens.count(); ++ii) { - if (mScreens.at(ii)->geometry() == QRect(0, 0, 0, 0)) - screensReady = false; - } - - if (!screensReady) - blockingReadEvents(); - else - return; + using T::T; + ~WithDestructor() + { + f(this->object()); } -} +}; void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uint32_t version) { struct ::wl_registry *registry = object(); - if (interface == QStringLiteral("wl_output")) { - mWaitingScreens << new QWaylandScreen(this, version, id); - } else if (interface == QStringLiteral("wl_compositor")) { - mCompositorVersion = qMin((int)version, 3); - mCompositor.init(registry, id, mCompositorVersion); - } else if (interface == QStringLiteral("wl_shm")) { - mShm.reset(new QWaylandShm(this, version, id)); - } else if (interface == QStringLiteral("wl_seat")) { + static QStringList interfaceBlacklist = qEnvironmentVariable("QT_WAYLAND_DISABLED_INTERFACES").split(u','); + if (interfaceBlacklist.contains(interface)) { + return; + } + + if (interface == QLatin1String(QtWayland::wl_output::interface()->name)) { + mWaitingScreens << mWaylandIntegration->createPlatformScreen(this, version, id); + } else if (interface == QLatin1String(QtWayland::wl_compositor::interface()->name)) { + mGlobals.compositor.reset( + new WithDestructor<QtWayland::wl_compositor, wl_compositor_destroy>( + registry, id, qMin((int)version, 6))); + } else if (interface == QLatin1String(QWaylandShm::interface()->name)) { + mGlobals.shm.reset(new QWaylandShm(this, version, id)); + } else if (interface == QLatin1String(QWaylandInputDevice::interface()->name)) { QWaylandInputDevice *inputDevice = mWaylandIntegration->createInputDevice(this, version, id); mInputDevices.append(inputDevice); #if QT_CONFIG(wayland_datadevice) - } else if (interface == QStringLiteral("wl_data_device_manager")) { - mDndSelectionHandler.reset(new QWaylandDataDeviceManager(this, id)); + } else if (interface == QLatin1String(QWaylandDataDeviceManager::interface()->name)) { + mGlobals.dndSelectionHandler.reset(new QWaylandDataDeviceManager(this, version, id)); +#endif + } else if (interface == QLatin1String(QtWayland::wl_subcompositor::interface()->name)) { + mGlobals.subCompositor.reset( + new WithDestructor<QtWayland::wl_subcompositor, wl_subcompositor_destroy>(registry, + id, 1)); + } else if (interface == QLatin1String(QWaylandTouchExtension::interface()->name)) { + mGlobals.touchExtension.reset(new QWaylandTouchExtension(this, id)); + } else if (interface == QLatin1String(QWaylandQtKeyExtension::interface()->name)) { + mGlobals.qtKeyExtension.reset(new QWaylandQtKeyExtension(this, id)); +#if QT_CONFIG(tabletevent) + } else if (interface == QLatin1String(QWaylandTabletManagerV2::interface()->name)) { + mGlobals.tabletManager.reset(new QWaylandTabletManagerV2(this, id, qMin(1, int(version)))); #endif - } else if (interface == QStringLiteral("qt_surface_extension")) { - mWindowExtension.reset(new QtWayland::qt_surface_extension(registry, id, 1)); - } else if (interface == QStringLiteral("wl_subcompositor")) { - mSubCompositor.reset(new QtWayland::wl_subcompositor(registry, id, 1)); - } else if (interface == QStringLiteral("qt_touch_extension")) { - mTouchExtension.reset(new QWaylandTouchExtension(this, id)); - } else if (interface == QStringLiteral("zqt_key_v1")) { - mQtKeyExtension.reset(new QWaylandQtKeyExtension(this, id)); + } else if (interface == QLatin1String(QWaylandPointerGestures::interface()->name)) { + mGlobals.pointerGestures.reset(new QWaylandPointerGestures(this, id, 1)); #if QT_CONFIG(wayland_client_primary_selection) - } else if (interface == QStringLiteral("zwp_primary_selection_device_manager_v1")) { - mPrimarySelectionManager.reset(new QWaylandPrimarySelectionDeviceManagerV1(this, id, 1)); + } else if (interface == QLatin1String(QWaylandPrimarySelectionDeviceManagerV1::interface()->name)) { + mGlobals.primarySelectionManager.reset( + new QWaylandPrimarySelectionDeviceManagerV1(this, id, 1)); + for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) + inputDevice->setPrimarySelectionDevice( + mGlobals.primarySelectionManager->createDevice(inputDevice)); #endif - } else if (interface == QStringLiteral("zwp_text_input_manager_v2") && !mClientSideInputContextRequested) { - mTextInputManager.reset(new QtWayland::zwp_text_input_manager_v2(registry, id, 1)); - for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices)) - inputDevice->setTextInput(new QWaylandTextInput(this, mTextInputManager->get_text_input(inputDevice->wl_seat()))); + } else if (interface == QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name) + && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) { + qCDebug(lcQpaWayland) << "text input: register qt_text_input_method_manager_v1"; + if (mTextInputManagerIndex < INT_MAX) { + mGlobals.textInputManagerv1.reset(); + mGlobals.textInputManagerv2.reset(); + mGlobals.textInputManagerv3.reset(); + for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) + inputDevice->setTextInput(nullptr); + } + + mGlobals.textInputMethodManager.reset( + new WithDestructor<QtWayland::qt_text_input_method_manager_v1, + qt_text_input_method_manager_v1_destroy>(registry, id, 1)); + for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) + inputDevice->setTextInputMethod(new QWaylandTextInputMethod( + this, + mGlobals.textInputMethodManager->get_text_input_method( + inputDevice->wl_seat()))); + mWaylandIntegration->reconfigureInputContext(); + mTextInputManagerIndex = mTextInputManagerList.indexOf(interface); + } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v1::interface()->name) + && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) { + qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v1"; + if (mTextInputManagerIndex < INT_MAX) { + mGlobals.textInputMethodManager.reset(); + mGlobals.textInputManagerv2.reset(); + mGlobals.textInputManagerv3.reset(); + for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) + inputDevice->setTextInputMethod(nullptr); + } + + mGlobals.textInputManagerv1.reset( + new WithDestructor<QtWayland::zwp_text_input_manager_v1, + zwp_text_input_manager_v1_destroy>(registry, id, 1)); + for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) { + auto textInput = + new QWaylandTextInputv1(this, mGlobals.textInputManagerv1->create_text_input()); + textInput->setSeat(inputDevice->wl_seat()); + inputDevice->setTextInput(textInput); + } + + mWaylandIntegration->reconfigureInputContext(); + mTextInputManagerIndex = mTextInputManagerList.indexOf(interface); + } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name) + && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) { + qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v2"; + if (mTextInputManagerIndex < INT_MAX) { + mGlobals.textInputMethodManager.reset(); + mGlobals.textInputManagerv1.reset(); + mGlobals.textInputManagerv3.reset(); + for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) + inputDevice->setTextInputMethod(nullptr); + } + + mGlobals.textInputManagerv2.reset( + new WithDestructor<QtWayland::zwp_text_input_manager_v2, + zwp_text_input_manager_v2_destroy>(registry, id, 1)); + for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) + inputDevice->setTextInput(new QWaylandTextInputv2( + this, mGlobals.textInputManagerv2->get_text_input(inputDevice->wl_seat()))); + mWaylandIntegration->reconfigureInputContext(); + mTextInputManagerIndex = mTextInputManagerList.indexOf(interface); + } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v3::interface()->name) + && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) { + qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v3"; + if (mTextInputManagerIndex < INT_MAX) { + mGlobals.textInputMethodManager.reset(); + mGlobals.textInputManagerv2.reset(); + for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) + inputDevice->setTextInputMethod(nullptr); + } + mGlobals.textInputManagerv3.reset( + new WithDestructor<QtWayland::zwp_text_input_manager_v3, + zwp_text_input_manager_v3_destroy>(registry, id, 1)); + for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) + inputDevice->setTextInput(new QWaylandTextInputv3( + this, mGlobals.textInputManagerv3->get_text_input(inputDevice->wl_seat()))); + mWaylandIntegration->reconfigureInputContext(); - } else if (interface == QStringLiteral("qt_hardware_integration")) { + mTextInputManagerIndex = mTextInputManagerList.indexOf(interface); + }else if (interface == QLatin1String(QWaylandHardwareIntegration::interface()->name)) { bool disableHardwareIntegration = qEnvironmentVariableIntValue("QT_WAYLAND_DISABLE_HW_INTEGRATION"); if (!disableHardwareIntegration) { - mHardwareIntegration.reset(new QWaylandHardwareIntegration(registry, id)); + mGlobals.hardwareIntegration.reset(new QWaylandHardwareIntegration(registry, id)); // make a roundtrip here since we need to receive the events sent by // qt_hardware_integration before creating windows forceRoundTrip(); } - } else if (interface == QLatin1String("zxdg_output_manager_v1")) { - mXdgOutputManager.reset(new QtWayland::zxdg_output_manager_v1(registry, id, qMin(2, int(version)))); - for (auto *screen : qAsConst(mWaitingScreens)) + } else if (interface == QLatin1String(QWaylandXdgOutputManagerV1::interface()->name)) { + mGlobals.xdgOutputManager.reset(new QWaylandXdgOutputManagerV1(this, id, version)); + for (auto *screen : std::as_const(mWaitingScreens)) screen->initXdgOutput(xdgOutputManager()); - forceRoundTrip(); + } else if (interface == QLatin1String(QtWayland::wp_fractional_scale_manager_v1::interface()->name)) { + mGlobals.fractionalScaleManager.reset( + new WithDestructor<QtWayland::wp_fractional_scale_manager_v1, + wp_fractional_scale_manager_v1_destroy>(registry, id, 1)); + } else if (interface == QLatin1String("wp_viewporter")) { + mGlobals.viewporter.reset( + new WithDestructor<QtWayland::wp_viewporter, wp_viewporter_destroy>( + registry, id, qMin(1u, version))); + } else if (interface == QLatin1String(QtWayland::wp_cursor_shape_manager_v1::interface()->name)) { + mGlobals.cursorShapeManager.reset(new WithDestructor<QtWayland::wp_cursor_shape_manager_v1, + wp_cursor_shape_manager_v1_destroy>( + registry, id, std::min(1u, version))); + } else if ( + interface == QLatin1String(QtWayland::xdg_toplevel_drag_manager_v1::interface()->name)) { + mGlobals.xdgToplevelDragManager.reset( + new WithDestructor<QtWayland::xdg_toplevel_drag_manager_v1, + xdg_toplevel_drag_manager_v1_destroy>(registry, id, 1)); + } else if (interface == QLatin1String(QtWayland::qt_windowmanager::interface()->name)) { + mGlobals.windowManagerIntegration.reset( + new QWaylandWindowManagerIntegration(this, id, version)); } - mGlobals.append(RegistryGlobal(id, interface, version, registry)); + mRegistryGlobals.append(RegistryGlobal(id, interface, version, registry)); + emit globalAdded(mRegistryGlobals.back()); const auto copy = mRegistryListeners; // be prepared for listeners unregistering on notification for (Listener l : copy) @@ -347,10 +788,10 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin void QWaylandDisplay::registry_global_remove(uint32_t id) { - for (int i = 0, ie = mGlobals.count(); i != ie; ++i) { - RegistryGlobal &global = mGlobals[i]; + for (int i = 0, ie = mRegistryGlobals.size(); i != ie; ++i) { + RegistryGlobal &global = mRegistryGlobals[i]; if (global.id == id) { - if (global.interface == QStringLiteral("wl_output")) { + if (global.interface == QLatin1String(QtWayland::wl_output::interface()->name)) { for (auto *screen : mWaitingScreens) { if (screen->outputId() == id) { mWaitingScreens.removeOne(screen); @@ -359,21 +800,48 @@ void QWaylandDisplay::registry_global_remove(uint32_t id) } } - for (QWaylandScreen *screen : qAsConst(mScreens)) { + for (QWaylandScreen *screen : std::as_const(mScreens)) { if (screen->outputId() == id) { mScreens.removeOne(screen); + // If this is the last screen, we have to add a fake screen, or Qt will break. + ensureScreen(); QWindowSystemInterface::handleScreenRemoved(screen); break; } } } - if (global.interface == QStringLiteral("zwp_text_input_manager_v2")) { - mTextInputManager.reset(); - for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices)) + if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v1::interface()->name)) { + mGlobals.textInputManagerv1.reset(); + for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) + inputDevice->setTextInput(nullptr); + mWaylandIntegration->reconfigureInputContext(); + } + if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name)) { + mGlobals.textInputManagerv2.reset(); + for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) + inputDevice->setTextInput(nullptr); + mWaylandIntegration->reconfigureInputContext(); + } + if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v3::interface()->name)) { + mGlobals.textInputManagerv3.reset(); + for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) inputDevice->setTextInput(nullptr); mWaylandIntegration->reconfigureInputContext(); } - mGlobals.removeAt(i); + if (global.interface == QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name)) { + mGlobals.textInputMethodManager.reset(); + for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) + inputDevice->setTextInputMethod(nullptr); + mWaylandIntegration->reconfigureInputContext(); + } +#if QT_CONFIG(wayland_client_primary_selection) + if (global.interface == QLatin1String(QtWayland::zwp_primary_selection_device_manager_v1::interface()->name)) { + mGlobals.primarySelectionManager.reset(); + for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) + inputDevice->setPrimarySelectionDevice(nullptr); + } +#endif + emit globalRemoved(mRegistryGlobals.takeAt(i)); break; } } @@ -381,7 +849,7 @@ void QWaylandDisplay::registry_global_remove(uint32_t id) bool QWaylandDisplay::hasRegistryGlobal(QStringView interfaceName) const { - for (const RegistryGlobal &global : mGlobals) + for (const RegistryGlobal &global : mRegistryGlobals) if (global.interface == interfaceName) return true; @@ -392,71 +860,22 @@ void QWaylandDisplay::addRegistryListener(RegistryListener listener, void *data) { Listener l = { listener, data }; mRegistryListeners.append(l); - for (int i = 0, ie = mGlobals.count(); i != ie; ++i) - (*l.listener)(l.data, mGlobals[i].registry, mGlobals[i].id, mGlobals[i].interface, mGlobals[i].version); + for (int i = 0, ie = mRegistryGlobals.size(); i != ie; ++i) + (*l.listener)(l.data, mRegistryGlobals[i].registry, mRegistryGlobals[i].id, + mRegistryGlobals[i].interface, mRegistryGlobals[i].version); } void QWaylandDisplay::removeListener(RegistryListener listener, void *data) { - std::remove_if(mRegistryListeners.begin(), mRegistryListeners.end(), [=](Listener l){ + auto iter = std::remove_if(mRegistryListeners.begin(), mRegistryListeners.end(), [=](Listener l){ return (l.listener == listener && l.data == data); }); + mRegistryListeners.erase(iter, mRegistryListeners.end()); } -uint32_t QWaylandDisplay::currentTimeMillisec() -{ - //### we throw away the time information - struct timeval tv; - int ret = gettimeofday(&tv, nullptr); - if (ret == 0) - return tv.tv_sec*1000 + tv.tv_usec/1000; - return 0; -} - -static void -sync_callback(void *data, struct wl_callback *callback, uint32_t serial) -{ - Q_UNUSED(serial) - bool *done = static_cast<bool *>(data); - - *done = true; - - // If the wl_callback done event is received after the condition check in the while loop in - // forceRoundTrip(), but before the call to processEvents, the call to processEvents may block - // forever if no more events are posted (eventhough the callback is handled in response to the - // aboutToBlock signal). Hence, we wake up the event dispatcher so forceRoundTrip may return. - // (QTBUG-64696) - if (auto *dispatcher = QThread::currentThread()->eventDispatcher()) - dispatcher->wakeUp(); - - wl_callback_destroy(callback); -} - -static const struct wl_callback_listener sync_listener = { - sync_callback -}; - void QWaylandDisplay::forceRoundTrip() { - // wl_display_roundtrip() works on the main queue only, - // but we use a separate one, so basically reimplement it here - int ret = 0; - bool done = false; - wl_callback *callback = wl_display_sync(mDisplay); - wl_callback_add_listener(callback, &sync_listener, &done); - flushRequests(); - if (QThread::currentThread()->eventDispatcher()) { - while (!done && ret >= 0) { - QThread::currentThread()->eventDispatcher()->processEvents(QEventLoop::WaitForMoreEvents); - ret = wl_display_dispatch_pending(mDisplay); - } - } else { - while (!done && ret >= 0) - ret = wl_display_dispatch(mDisplay); - } - - if (ret == -1 && !done) - wl_callback_destroy(callback); + wl_display_roundtrip(mDisplay); } bool QWaylandDisplay::supportsWindowDecoration() const @@ -467,7 +886,12 @@ bool QWaylandDisplay::supportsWindowDecoration() const if (disabled) return false; - static bool integrationSupport = clientBufferIntegration() && clientBufferIntegration()->supportsWindowDecoration(); + // Don't initialize client buffer integration just to check whether it can have a decoration. + if (!mWaylandIntegration->mClientBufferIntegrationInitialized) + return true; + + // We can do software-rendered decorations, only disable them if the integration explicitly says it can't. + static bool integrationSupport = !clientBufferIntegration() || clientBufferIntegration()->supportsWindowDecoration(); return integrationSupport; } @@ -509,6 +933,9 @@ void QWaylandDisplay::handleWindowDeactivated(QWaylandWindow *window) mActiveWindows.removeOne(window); + if (QCoreApplication::closingDown()) + return; + if (auto *decoration = window->decoration()) decoration->update(); } @@ -520,14 +947,10 @@ void QWaylandDisplay::handleKeyboardFocusChanged(QWaylandInputDevice *inputDevic if (mLastKeyboardFocus == keyboardFocus) return; - if (mWaylandIntegration->mShellIntegration) { - mWaylandIntegration->mShellIntegration->handleKeyboardFocusChanged(keyboardFocus, mLastKeyboardFocus); - } else { - if (keyboardFocus) - handleWindowActivated(keyboardFocus); - if (mLastKeyboardFocus) - handleWindowDeactivated(mLastKeyboardFocus); - } + if (keyboardFocus) + handleWindowActivated(keyboardFocus); + if (mLastKeyboardFocus) + handleWindowDeactivated(mLastKeyboardFocus); mLastKeyboardFocus = keyboardFocus; } @@ -545,7 +968,20 @@ void QWaylandDisplay::handleWaylandSync() // handleWindowActivated() calls immediately. QWindow *activeWindow = mActiveWindows.empty() ? nullptr : mActiveWindows.last()->window(); if (activeWindow != QGuiApplication::focusWindow()) - QWindowSystemInterface::handleWindowActivated(activeWindow); + QWindowSystemInterface::handleFocusWindowChanged(activeWindow); + + if (!activeWindow) { + if (lastInputDevice()) { +#if QT_CONFIG(clipboard) + if (auto *dataDevice = lastInputDevice()->dataDevice()) + dataDevice->invalidateSelectionOffer(); +#endif +#if QT_CONFIG(wayland_client_primary_selection) + if (auto *device = lastInputDevice()->primarySelectionDevice()) + device->invalidateSelectionOffer(); +#endif + } + } } const wl_callback_listener QWaylandDisplay::syncCallbackListener = { @@ -569,7 +1005,18 @@ void QWaylandDisplay::requestWaylandSync() QWaylandInputDevice *QWaylandDisplay::defaultInputDevice() const { - return mInputDevices.isEmpty() ? 0 : mInputDevices.first(); + return mInputDevices.isEmpty() ? nullptr : mInputDevices.first(); +} + +bool QWaylandDisplay::isKeyboardAvailable() const +{ + return std::any_of( + mInputDevices.constBegin(), mInputDevices.constEnd(), + [](const QWaylandInputDevice *device) { return device->keyboard() != nullptr; }); +} + +bool QWaylandDisplay::isWaylandInputContextRequested() const { + return mWaylandInputContextRequested; } #if QT_CONFIG(cursor) @@ -577,19 +1024,34 @@ QWaylandInputDevice *QWaylandDisplay::defaultInputDevice() const QWaylandCursor *QWaylandDisplay::waylandCursor() { if (!mCursor) - mCursor.reset(new QWaylandCursor(this)); + mCursor.reset(mWaylandIntegration->createPlatformCursor(this)); return mCursor.data(); } +auto QWaylandDisplay::findExistingCursorTheme(const QString &name, int pixelSize) const noexcept + -> FindExistingCursorThemeResult +{ + const auto byNameAndSize = [](const WaylandCursorTheme &lhs, const WaylandCursorTheme &rhs) { + return std::tie(lhs.pixelSize, lhs.name) < std::tie(rhs.pixelSize, rhs.name); + }; + + const WaylandCursorTheme prototype = {name, pixelSize, nullptr}; + + const auto it = std::lower_bound(mCursorThemes.cbegin(), mCursorThemes.cend(), prototype, byNameAndSize); + if (it != mCursorThemes.cend() && it->name == name && it->pixelSize == pixelSize) + return {it, true}; + else + return {it, false}; +} + QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(const QString &name, int pixelSize) { - if (auto *theme = mCursorThemes.value({name, pixelSize}, nullptr)) - return theme; + const auto result = findExistingCursorTheme(name, pixelSize); + if (result.found) + return result.theme(); - if (auto *theme = QWaylandCursorTheme::create(shm(), pixelSize, name)) { - mCursorThemes[{name, pixelSize}] = theme; - return theme; - } + if (auto theme = QWaylandCursorTheme::create(shm(), pixelSize, name)) + return mCursorThemes.insert(result.position, {name, pixelSize, std::move(theme)})->theme.get(); return nullptr; } @@ -599,3 +1061,6 @@ QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(const QString &name, int p } // namespace QtWaylandClient QT_END_NAMESPACE + +#include "qwaylanddisplay.moc" +#include "moc_qwaylanddisplay_p.cpp" diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h index 14bb77198..b93a2ecc0 100644 --- a/src/client/qwaylanddisplay_p.h +++ b/src/client/qwaylanddisplay_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDDISPLAY_H #define QWAYLANDDISPLAY_H @@ -51,10 +15,12 @@ // We mean it. // +#include <QtCore/QList> #include <QtCore/QObject> -#include <QtCore/QRect> #include <QtCore/QPointer> -#include <QtCore/QVector> +#include <QtCore/QRect> +#include <QtCore/QMutex> +#include <QtCore/QReadWriteLock> #include <QtCore/QWaitCondition> #include <QtCore/QLoggingCategory> @@ -66,36 +32,50 @@ #include <qpa/qplatforminputcontextfactory_p.h> #if QT_CONFIG(xkbcommon) -#include <QtXkbCommonSupport/private/qxkbcommon_p.h> +#include <QtGui/private/qxkbcommon_p.h> #endif struct wl_cursor_image; +struct wp_viewport; QT_BEGIN_NAMESPACE +#define WAYLAND_IM_KEY "wayland" + class QAbstractEventDispatcher; class QSocketNotifier; class QPlatformScreen; +class QPlatformPlaceholderScreen; namespace QtWayland { - class qt_surface_extension; + class zwp_text_input_manager_v1; class zwp_text_input_manager_v2; - class zxdg_output_manager_v1; + class zwp_text_input_manager_v3; + class qt_text_input_method_manager_v1; + class wp_cursor_shape_manager_v1; + class wp_fractional_scale_manager_v1; + class wp_viewporter; + class xdg_toplevel_drag_manager_v1; } namespace QtWaylandClient { -Q_WAYLAND_CLIENT_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcQpaWayland); +Q_WAYLANDCLIENT_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcQpaWayland); class QWaylandInputDevice; class QWaylandBuffer; class QWaylandScreen; +class QWaylandXdgOutputManagerV1; class QWaylandClientBufferIntegration; class QWaylandWindowManagerIntegration; class QWaylandDataDeviceManager; #if QT_CONFIG(wayland_client_primary_selection) class QWaylandPrimarySelectionDeviceManagerV1; #endif +#if QT_CONFIG(tabletevent) +class QWaylandTabletManagerV2; +#endif +class QWaylandPointerGestures; class QWaylandTouchExtension; class QWaylandQtKeyExtension; class QWaylandWindow; @@ -105,6 +85,7 @@ class QWaylandSurface; class QWaylandShellIntegration; class QWaylandCursor; class QWaylandCursorTheme; +class EventThread; typedef void (*RegistryListener)(void *data, struct wl_registry *registry, @@ -112,18 +93,22 @@ typedef void (*RegistryListener)(void *data, const QString &interface, uint32_t version); -class Q_WAYLAND_CLIENT_EXPORT QWaylandDisplay : public QObject, public QtWayland::wl_registry { +class Q_WAYLANDCLIENT_EXPORT QWaylandDisplay : public QObject, public QtWayland::wl_registry { Q_OBJECT public: QWaylandDisplay(QWaylandIntegration *waylandIntegration); ~QWaylandDisplay(void) override; + bool initialize(); + #if QT_CONFIG(xkbcommon) struct xkb_context *xkbContext() const { return mXkbContext.get(); } #endif QList<QWaylandScreen *> screens() const { return mScreens; } + QPlatformPlaceholderScreen *placeholderScreen() const { return mPlaceholderScreen; } + void ensureScreen(); QWaylandScreen *screenForOutput(struct wl_output *output) const; void handleScreenInitialized(QWaylandScreen *screen); @@ -131,6 +116,7 @@ public: struct wl_surface *createSurface(void *handle); struct ::wl_region *createRegion(const QRegion &qregion); struct ::wl_subsurface *createSubSurface(QWaylandWindow *window, QWaylandWindow *parent); + struct ::wp_viewport *createViewport(QWaylandWindow *window); QWaylandShellIntegration *shellIntegration() const; QWaylandClientBufferIntegration *clientBufferIntegration() const; @@ -140,29 +126,87 @@ public: QWaylandCursor *waylandCursor(); QWaylandCursorTheme *loadCursorTheme(const QString &name, int pixelSize); #endif - struct wl_display *wl_display() const { return mDisplay; } + struct wl_display *wl_display() const + { + return mDisplay; + } struct ::wl_registry *wl_registry() { return object(); } - const struct wl_compositor *wl_compositor() const { return mCompositor.object(); } - QtWayland::wl_compositor *compositor() { return &mCompositor; } - int compositorVersion() const { return mCompositorVersion; } + QtWayland::wl_compositor *compositor() + { + return mGlobals.compositor.get(); + } QList<QWaylandInputDevice *> inputDevices() const { return mInputDevices; } QWaylandInputDevice *defaultInputDevice() const; QWaylandInputDevice *currentInputDevice() const { return defaultInputDevice(); } #if QT_CONFIG(wayland_datadevice) - QWaylandDataDeviceManager *dndSelectionHandler() const { return mDndSelectionHandler.data(); } + QWaylandDataDeviceManager *dndSelectionHandler() const + { + return mGlobals.dndSelectionHandler.get(); + } #endif #if QT_CONFIG(wayland_client_primary_selection) - QWaylandPrimarySelectionDeviceManagerV1 *primarySelectionManager() const { return mPrimarySelectionManager.data(); } + QWaylandPrimarySelectionDeviceManagerV1 *primarySelectionManager() const + { + return mGlobals.primarySelectionManager.get(); + } #endif - QtWayland::qt_surface_extension *windowExtension() const { return mWindowExtension.data(); } - QWaylandTouchExtension *touchExtension() const { return mTouchExtension.data(); } - QtWayland::zwp_text_input_manager_v2 *textInputManager() const { return mTextInputManager.data(); } - QWaylandHardwareIntegration *hardwareIntegration() const { return mHardwareIntegration.data(); } - QtWayland::zxdg_output_manager_v1 *xdgOutputManager() const { return mXdgOutputManager.data(); } +#if QT_CONFIG(tabletevent) + QWaylandTabletManagerV2 *tabletManager() const + { + return mGlobals.tabletManager.get(); + } +#endif + QWaylandPointerGestures *pointerGestures() const + { + return mGlobals.pointerGestures.get(); + } + QWaylandTouchExtension *touchExtension() const + { + return mGlobals.touchExtension.get(); + } + QtWayland::qt_text_input_method_manager_v1 *textInputMethodManager() const + { + return mGlobals.textInputMethodManager.get(); + } + QtWayland::zwp_text_input_manager_v1 *textInputManagerv1() const + { + return mGlobals.textInputManagerv1.get(); + } + QtWayland::zwp_text_input_manager_v2 *textInputManagerv2() const + { + return mGlobals.textInputManagerv2.get(); + } + QtWayland::zwp_text_input_manager_v3 *textInputManagerv3() const + { + return mGlobals.textInputManagerv3.get(); + } + QWaylandHardwareIntegration *hardwareIntegration() const + { + return mGlobals.hardwareIntegration.get(); + } + QWaylandXdgOutputManagerV1 *xdgOutputManager() const + { + return mGlobals.xdgOutputManager.get(); + } + QtWayland::wp_fractional_scale_manager_v1 *fractionalScaleManager() const + { + return mGlobals.fractionalScaleManager.get(); + } + QtWayland::wp_viewporter *viewporter() const + { + return mGlobals.viewporter.get(); + } + QtWayland::wp_cursor_shape_manager_v1 *cursorShapeManager() const + { + return mGlobals.cursorShapeManager.get(); + } + QtWayland::xdg_toplevel_drag_manager_v1 *xdgToplevelDragManager() const + { + return mGlobals.xdgToplevelDragManager.get(); + } - bool usingInputContextFromCompositor() const { return mUsingInputContextFromCompositor; } struct RegistryGlobal { uint32_t id; @@ -172,7 +216,10 @@ public: RegistryGlobal(uint32_t id_, const QString &interface_, uint32_t version_, struct ::wl_registry *registry_) : id(id_), interface(interface_), version(version_), registry(registry_) { } }; - QList<RegistryGlobal> globals() const { return mGlobals; } + QList<RegistryGlobal> globals() const + { + return mRegistryGlobals; + } bool hasRegistryGlobal(QStringView interfaceName) const; /* wl_registry_add_listener does not add but rather sets a listener, so this function is used @@ -180,9 +227,10 @@ public: void addRegistryListener(RegistryListener listener, void *data); void removeListener(RegistryListener listener, void *data); - QWaylandShm *shm() const { return mShm.data(); } - - static uint32_t currentTimeMillisec(); + QWaylandShm *shm() const + { + return mGlobals.shm.get(); + } void forceRoundTrip(); @@ -199,20 +247,31 @@ public: void handleKeyboardFocusChanged(QWaylandInputDevice *inputDevice); void handleWindowDestroyed(QWaylandWindow *window); - wl_event_queue *createEventQueue(); - void dispatchQueueWhile(wl_event_queue *queue, std::function<bool()> condition, int timeout = -1); + wl_event_queue *frameEventQueue() { return m_frameEventQueue; }; + + bool isKeyboardAvailable() const; + bool isWaylandInputContextRequested() const; -public slots: + void initEventThread(); + +public Q_SLOTS: void blockingReadEvents(); void flushRequests(); -private: - void waitForScreens(); - void checkError() const; +Q_SIGNALS: + void connected(); + void globalAdded(const RegistryGlobal &global); + void globalRemoved(const RegistryGlobal &global); +private: + void checkWaylandError(); + void reconnect(); + void setupConnection(); void handleWaylandSync(); void requestWaylandSync(); + void checkTextInputProtocol(); + struct Listener { Listener() = default; Listener(RegistryListener incomingListener, @@ -222,48 +281,83 @@ private: RegistryListener listener = nullptr; void *data = nullptr; }; - struct wl_display *mDisplay = nullptr; - QtWayland::wl_compositor mCompositor; - QScopedPointer<QWaylandShm> mShm; + std::unique_ptr<EventThread> m_eventThread; + wl_event_queue *m_frameEventQueue = nullptr; + QScopedPointer<EventThread> m_frameEventQueueThread; QList<QWaylandScreen *> mWaitingScreens; QList<QWaylandScreen *> mScreens; + QPlatformPlaceholderScreen *mPlaceholderScreen = nullptr; QList<QWaylandInputDevice *> mInputDevices; QList<Listener> mRegistryListeners; QWaylandIntegration *mWaylandIntegration = nullptr; #if QT_CONFIG(cursor) - QMap<std::pair<QString, int>, QWaylandCursorTheme *> mCursorThemes; // theme name and size + struct WaylandCursorTheme { + QString name; + int pixelSize; + std::unique_ptr<QWaylandCursorTheme> theme; + }; + std::vector<WaylandCursorTheme> mCursorThemes; + + struct FindExistingCursorThemeResult { + std::vector<WaylandCursorTheme>::const_iterator position; + bool found; + + QWaylandCursorTheme *theme() const noexcept + { return found ? position->theme.get() : nullptr; } + }; + FindExistingCursorThemeResult findExistingCursorTheme(const QString &name, + int pixelSize) const noexcept; QScopedPointer<QWaylandCursor> mCursor; #endif + + struct GlobalHolder + { + std::unique_ptr<QtWayland::wl_compositor> compositor; + std::unique_ptr<QWaylandShm> shm; #if QT_CONFIG(wayland_datadevice) - QScopedPointer<QWaylandDataDeviceManager> mDndSelectionHandler; + std::unique_ptr<QWaylandDataDeviceManager> dndSelectionHandler; #endif - QScopedPointer<QtWayland::qt_surface_extension> mWindowExtension; - QScopedPointer<QtWayland::wl_subcompositor> mSubCompositor; - QScopedPointer<QWaylandTouchExtension> mTouchExtension; - QScopedPointer<QWaylandQtKeyExtension> mQtKeyExtension; - QScopedPointer<QWaylandWindowManagerIntegration> mWindowManagerIntegration; + std::unique_ptr<QtWayland::wl_subcompositor> subCompositor; + std::unique_ptr<QWaylandTouchExtension> touchExtension; + std::unique_ptr<QWaylandQtKeyExtension> qtKeyExtension; +#if QT_CONFIG(tabletevent) + std::unique_ptr<QWaylandTabletManagerV2> tabletManager; +#endif + std::unique_ptr<QWaylandPointerGestures> pointerGestures; #if QT_CONFIG(wayland_client_primary_selection) - QScopedPointer<QWaylandPrimarySelectionDeviceManagerV1> mPrimarySelectionManager; + std::unique_ptr<QWaylandPrimarySelectionDeviceManagerV1> primarySelectionManager; #endif - QScopedPointer<QtWayland::zwp_text_input_manager_v2> mTextInputManager; - QScopedPointer<QWaylandHardwareIntegration> mHardwareIntegration; - QScopedPointer<QtWayland::zxdg_output_manager_v1> mXdgOutputManager; - QSocketNotifier *mReadNotifier = nullptr; + std::unique_ptr<QtWayland::qt_text_input_method_manager_v1> textInputMethodManager; + std::unique_ptr<QtWayland::zwp_text_input_manager_v1> textInputManagerv1; + std::unique_ptr<QtWayland::zwp_text_input_manager_v2> textInputManagerv2; + std::unique_ptr<QtWayland::zwp_text_input_manager_v3> textInputManagerv3; + std::unique_ptr<QWaylandHardwareIntegration> hardwareIntegration; + std::unique_ptr<QWaylandXdgOutputManagerV1> xdgOutputManager; + std::unique_ptr<QtWayland::wp_viewporter> viewporter; + std::unique_ptr<QtWayland::wp_fractional_scale_manager_v1> fractionalScaleManager; + std::unique_ptr<QtWayland::wp_cursor_shape_manager_v1> cursorShapeManager; + std::unique_ptr<QtWayland::xdg_toplevel_drag_manager_v1> xdgToplevelDragManager; + std::unique_ptr<QWaylandWindowManagerIntegration> windowManagerIntegration; + } mGlobals; int mFd = -1; int mWritableNotificationFd = -1; - QList<RegistryGlobal> mGlobals; - int mCompositorVersion = -1; + QList<RegistryGlobal> mRegistryGlobals; uint32_t mLastInputSerial = 0; QWaylandInputDevice *mLastInputDevice = nullptr; QPointer<QWaylandWindow> mLastInputWindow; QPointer<QWaylandWindow> mLastKeyboardFocus; - QVector<QWaylandWindow *> mActiveWindows; + QList<QWaylandWindow *> mActiveWindows; struct wl_callback *mSyncCallback = nullptr; static const wl_callback_listener syncCallbackListener; - - bool mClientSideInputContextRequested = !QPlatformInputContextFactory::requested().isNull(); - bool mUsingInputContextFromCompositor = false; + bool mWaylandTryReconnect = false; + + bool mWaylandInputContextRequested = [] () { + const auto requested = QPlatformInputContextFactory::requested(); + return requested.isEmpty() || requested.contains(QLatin1String(WAYLAND_IM_KEY)); + }(); + QStringList mTextInputManagerList; + int mTextInputManagerIndex = INT_MAX; void registry_global(uint32_t id, const QString &interface, uint32_t version) override; void registry_global_remove(uint32_t id) override; diff --git a/src/client/qwaylanddnd.cpp b/src/client/qwaylanddnd.cpp index 6535aa16b..5ea1e0d33 100644 --- a/src/client/qwaylanddnd.cpp +++ b/src/client/qwaylanddnd.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylanddnd_p.h" @@ -66,7 +30,7 @@ void QWaylandDrag::startDrag() { QBasicDrag::startDrag(); QWaylandWindow *icon = static_cast<QWaylandWindow *>(shapedPixmapWindow()->handle()); - if (m_display->currentInputDevice()->dataDevice()->startDrag(drag()->mimeData(), icon)) { + if (m_display->currentInputDevice()->dataDevice()->startDrag(drag()->mimeData(), drag()->supportedActions(), icon)) { icon->addAttachOffset(-drag()->hotSpot()); } else { // Cancelling immediately does not work, since the event loop for QDrag::exec is started @@ -80,6 +44,9 @@ void QWaylandDrag::cancel() QBasicDrag::cancel(); m_display->currentInputDevice()->dataDevice()->cancelDrag(); + + if (drag()) + drag()->deleteLater(); } void QWaylandDrag::move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) @@ -92,10 +59,7 @@ void QWaylandDrag::move(const QPoint &globalPos, Qt::MouseButtons b, Qt::Keyboar void QWaylandDrag::drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) { - Q_UNUSED(globalPos); - Q_UNUSED(b); - Q_UNUSED(mods); - // Do nothing + QBasicDrag::drop(globalPos, b, mods); } void QWaylandDrag::endDrag() @@ -103,33 +67,41 @@ void QWaylandDrag::endDrag() m_display->currentInputDevice()->handleEndDrag(); } -void QWaylandDrag::updateTarget(const QString &mimeType) +void QWaylandDrag::setResponse(bool accepted) { - setCanDrop(!mimeType.isEmpty()); - - if (canDrop()) { - updateCursor(defaultAction(drag()->supportedActions(), m_display->currentInputDevice()->modifiers())); - } else { - updateCursor(Qt::IgnoreAction); - } + // This method is used for old DataDevices where the drag action is not communicated + Qt::DropAction action = defaultAction(drag()->supportedActions(), m_display->currentInputDevice()->modifiers()); + setResponse(QPlatformDropQtResponse(accepted, action)); } -void QWaylandDrag::setResponse(const QPlatformDragQtResponse &response) +void QWaylandDrag::setResponse(const QPlatformDropQtResponse &response) { setCanDrop(response.isAccepted()); if (canDrop()) { - updateCursor(defaultAction(drag()->supportedActions(), m_display->currentInputDevice()->modifiers())); + updateCursor(response.acceptedAction()); } else { updateCursor(Qt::IgnoreAction); } } -void QWaylandDrag::finishDrag(const QPlatformDropQtResponse &response) +void QWaylandDrag::setDropResponse(const QPlatformDropQtResponse &response) { setExecutedDropAction(response.acceptedAction()); +} + +void QWaylandDrag::finishDrag() +{ QKeyEvent event(QEvent::KeyPress, Qt::Key_Escape, Qt::NoModifier); eventFilter(shapedPixmapWindow(), &event); + + if (drag()) + drag()->deleteLater(); +} + +bool QWaylandDrag::ownsDragObject() const +{ + return true; } } diff --git a/src/client/qwaylanddnd_p.h b/src/client/qwaylanddnd_p.h index 474fe2ab1..4952c6d3d 100644 --- a/src/client/qwaylanddnd_p.h +++ b/src/client/qwaylanddnd_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDDND_H #define QWAYLANDDND_H @@ -65,15 +29,16 @@ namespace QtWaylandClient { class QWaylandDisplay; #if QT_CONFIG(draganddrop) -class Q_WAYLAND_CLIENT_EXPORT QWaylandDrag : public QBasicDrag +class Q_WAYLANDCLIENT_EXPORT QWaylandDrag : public QBasicDrag { public: QWaylandDrag(QWaylandDisplay *display); ~QWaylandDrag() override; - void updateTarget(const QString &mimeType); - void setResponse(const QPlatformDragQtResponse &response); - void finishDrag(const QPlatformDropQtResponse &response); + void setResponse(bool accepted); + void setResponse(const QPlatformDropQtResponse &response); + void setDropResponse(const QPlatformDropQtResponse &response); + void finishDrag(); protected: void startDrag() override; @@ -82,6 +47,7 @@ protected: void drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override; void endDrag() override; + bool ownsDragObject() const override; private: QWaylandDisplay *m_display = nullptr; diff --git a/src/client/qwaylandextendedsurface.cpp b/src/client/qwaylandextendedsurface.cpp deleted file mode 100644 index a7836e292..000000000 --- a/src/client/qwaylandextendedsurface.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qwaylandextendedsurface_p.h" - -#include "qwaylandwindow_p.h" - -#include "qwaylanddisplay_p.h" - -#include "qwaylandnativeinterface_p.h" - -#include <QtGui/QGuiApplication> -#include <qpa/qplatformnativeinterface.h> -#include <qpa/qwindowsysteminterface.h> - -QT_BEGIN_NAMESPACE - -namespace QtWaylandClient { - -QWaylandExtendedSurface::QWaylandExtendedSurface(QWaylandWindow *window) - : QtWayland::qt_extended_surface(window->display()->windowExtension()->get_extended_surface(window->wlSurface())) - , m_window(window) -{ -} - -QWaylandExtendedSurface::~QWaylandExtendedSurface() -{ - qt_extended_surface_destroy(object()); -} - -void QWaylandExtendedSurface::updateGenericProperty(const QString &name, const QVariant &value) -{ - QByteArray byteValue; - QDataStream ds(&byteValue, QIODevice::WriteOnly); - ds << value; - - update_generic_property(name, byteValue); -} - -void QWaylandExtendedSurface::setContentOrientationMask(Qt::ScreenOrientations mask) -{ - int32_t wlmask = 0; - if (mask & Qt::PrimaryOrientation) - wlmask |= QT_EXTENDED_SURFACE_ORIENTATION_PRIMARYORIENTATION; - if (mask & Qt::PortraitOrientation) - wlmask |= QT_EXTENDED_SURFACE_ORIENTATION_PORTRAITORIENTATION; - if (mask & Qt::LandscapeOrientation) - wlmask |= QT_EXTENDED_SURFACE_ORIENTATION_LANDSCAPEORIENTATION; - if (mask & Qt::InvertedPortraitOrientation) - wlmask |= QT_EXTENDED_SURFACE_ORIENTATION_INVERTEDPORTRAITORIENTATION; - if (mask & Qt::InvertedLandscapeOrientation) - wlmask |= QT_EXTENDED_SURFACE_ORIENTATION_INVERTEDLANDSCAPEORIENTATION; - set_content_orientation_mask(wlmask); -} - -void QWaylandExtendedSurface::extended_surface_onscreen_visibility(int32_t visibility) -{ - m_window->window()->setVisibility(static_cast<QWindow::Visibility>(visibility)); -} - -void QWaylandExtendedSurface::extended_surface_set_generic_property(const QString &name, wl_array *value) -{ - QByteArray data = QByteArray::fromRawData(static_cast<char *>(value->data), value->size); - - QVariant variantValue; - QDataStream ds(data); - ds >> variantValue; - - m_window->setProperty(name, variantValue); -} - -void QWaylandExtendedSurface::extended_surface_close() -{ - QWindowSystemInterface::handleCloseEvent(m_window->window()); -} - -Qt::WindowFlags QWaylandExtendedSurface::setWindowFlags(Qt::WindowFlags flags) -{ - uint wlFlags = 0; - - if (flags & Qt::WindowStaysOnTopHint) wlFlags |= QT_EXTENDED_SURFACE_WINDOWFLAG_STAYSONTOP; - if (flags & Qt::WindowOverridesSystemGestures) wlFlags |= QT_EXTENDED_SURFACE_WINDOWFLAG_OVERRIDESSYSTEMGESTURES; - if (flags & Qt::BypassWindowManagerHint) wlFlags |= QT_EXTENDED_SURFACE_WINDOWFLAG_BYPASSWINDOWMANAGER; - - set_window_flags(wlFlags); - - return flags & (Qt::WindowStaysOnTopHint | Qt::WindowOverridesSystemGestures | Qt::BypassWindowManagerHint); -} - -} - -QT_END_NAMESPACE diff --git a/src/client/qwaylandextendedsurface_p.h b/src/client/qwaylandextendedsurface_p.h deleted file mode 100644 index d71ac6be9..000000000 --- a/src/client/qwaylandextendedsurface_p.h +++ /dev/null @@ -1,93 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QWAYLANDEXTENDEDSURFACE_H -#define QWAYLANDEXTENDEDSURFACE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/QString> -#include <QtCore/QVariant> - -#include <QtWaylandClient/qtwaylandclientglobal.h> - -#include <QtWaylandClient/private/qwayland-surface-extension.h> - -QT_BEGIN_NAMESPACE - -namespace QtWaylandClient { - -class QWaylandDisplay; -class QWaylandWindow; - -class Q_WAYLAND_CLIENT_EXPORT QWaylandExtendedSurface : public QtWayland::qt_extended_surface -{ -public: - QWaylandExtendedSurface(QWaylandWindow *window); - ~QWaylandExtendedSurface() override; - - void setContentOrientationMask(Qt::ScreenOrientations mask); - - void updateGenericProperty(const QString &name, const QVariant &value); - - Qt::WindowFlags setWindowFlags(Qt::WindowFlags flags); - -private: - void extended_surface_onscreen_visibility(int32_t visibility) override; - void extended_surface_set_generic_property(const QString &name, wl_array *value) override; - void extended_surface_close() override; - - QWaylandWindow *m_window = nullptr; - QVariantMap m_properties; -}; - -} - -QT_END_NAMESPACE - -#endif // QWAYLANDEXTENDEDSURFACE_H diff --git a/src/client/qwaylandfractionalscale.cpp b/src/client/qwaylandfractionalscale.cpp new file mode 100644 index 000000000..6cd933f47 --- /dev/null +++ b/src/client/qwaylandfractionalscale.cpp @@ -0,0 +1,31 @@ +// Copyright (C) 2022 David Edmundson <davidedmundson@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwaylandfractionalscale_p.h" + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +QWaylandFractionalScale::QWaylandFractionalScale(struct ::wp_fractional_scale_v1 *object) + : QtWayland::wp_fractional_scale_v1(object) +{} + + +QWaylandFractionalScale::~QWaylandFractionalScale() +{ + destroy(); +} + +void QWaylandFractionalScale::wp_fractional_scale_v1_preferred_scale(uint scale) +{ + qreal preferredScale = scale / 120.0; // hardcoded denominator determined in the spec + if (preferredScale != mPreferredScale) { + mPreferredScale = preferredScale; + Q_EMIT preferredScaleChanged(); + } +} + +} + +QT_END_NAMESPACE diff --git a/src/client/qwaylandfractionalscale_p.h b/src/client/qwaylandfractionalscale_p.h new file mode 100644 index 000000000..48e1fb487 --- /dev/null +++ b/src/client/qwaylandfractionalscale_p.h @@ -0,0 +1,52 @@ +// Copyright (C) 2022 David Edmundson <davidedmundson@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWAYLANDFRACTIONALSCALE_P_H +#define QWAYLANDFRACTIONALSCALE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtWaylandClient/private/qwayland-fractional-scale-v1.h> +#include <QtWaylandClient/qtwaylandclientglobal.h> + +#include <QObject> + +#include <optional> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class QWaylandFractionalScale : public QObject, public QtWayland::wp_fractional_scale_v1 +{ + Q_OBJECT +public: + explicit QWaylandFractionalScale(struct ::wp_fractional_scale_v1 *object); + ~QWaylandFractionalScale(); + + std::optional<qreal> preferredScale() const { return mPreferredScale; } + +Q_SIGNALS: + void preferredScaleChanged(); + +protected: + void wp_fractional_scale_v1_preferred_scale(uint scale) override; + +private: + std::optional<qreal> mPreferredScale; +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/client/qwaylandinputcontext.cpp b/src/client/qwaylandinputcontext.cpp index e9afe05ed..a38bb9a0a 100644 --- a/src/client/qwaylandinputcontext.cpp +++ b/src/client/qwaylandinputcontext.cpp @@ -1,393 +1,28 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWaylandClient module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandinputcontext_p.h" +#include <QLoggingCategory> #include <QtGui/QGuiApplication> #include <QtGui/QTextCharFormat> #include <QtGui/QWindow> -#include <QtGui/private/qguiapplication_p.h> -#include <QtGui/qpa/qplatformintegration.h> +#include <QtCore/QVarLengthArray> #include "qwaylanddisplay_p.h" #include "qwaylandinputdevice_p.h" -#include "qwaylandinputmethodeventbuilder_p.h" #include "qwaylandwindow_p.h" -QT_BEGIN_NAMESPACE - -Q_LOGGING_CATEGORY(qLcQpaInputMethods, "qt.qpa.input.methods") - -namespace QtWaylandClient { - -namespace { -const Qt::InputMethodQueries supportedQueries = Qt::ImEnabled | - Qt::ImSurroundingText | - Qt::ImCursorPosition | - Qt::ImAnchorPosition | - Qt::ImHints | - Qt::ImCursorRectangle | - Qt::ImPreferredLanguage; -} - -QWaylandTextInput::QWaylandTextInput(QWaylandDisplay *display, struct ::zwp_text_input_v2 *text_input) - : QtWayland::zwp_text_input_v2(text_input) - , m_display(display) -{ -} - -QWaylandTextInput::~QWaylandTextInput() -{ - if (m_resetCallback) - wl_callback_destroy(m_resetCallback); -} - -void QWaylandTextInput::reset() -{ - m_builder.reset(); - m_preeditCommit = QString(); - updateState(Qt::ImQueryAll, update_state_reset); -} - -void QWaylandTextInput::commit() -{ - if (QObject *o = QGuiApplication::focusObject()) { - QInputMethodEvent event; - event.setCommitString(m_preeditCommit); - QCoreApplication::sendEvent(o, &event); - } - - reset(); -} - -const wl_callback_listener QWaylandTextInput::callbackListener = { - QWaylandTextInput::resetCallback -}; - -void QWaylandTextInput::resetCallback(void *data, wl_callback *, uint32_t) -{ - QWaylandTextInput *self = static_cast<QWaylandTextInput*>(data); - - if (self->m_resetCallback) { - wl_callback_destroy(self->m_resetCallback); - self->m_resetCallback = nullptr; - } -} - -void QWaylandTextInput::updateState(Qt::InputMethodQueries queries, uint32_t flags) -{ - if (!QGuiApplication::focusObject()) - return; - - if (!QGuiApplication::focusWindow() || !QGuiApplication::focusWindow()->handle()) - return; - - auto *window = static_cast<QWaylandWindow *>(QGuiApplication::focusWindow()->handle()); - auto *surface = window->wlSurface(); - if (!surface || (surface != m_surface)) - return; - - queries &= supportedQueries; - - // Surrounding text, cursor and anchor positions are transferred together - if ((queries & Qt::ImSurroundingText) || (queries & Qt::ImCursorPosition) || (queries & Qt::ImAnchorPosition)) - queries |= Qt::ImSurroundingText | Qt::ImCursorPosition | Qt::ImAnchorPosition; - - QInputMethodQueryEvent event(queries); - QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event); - - if ((queries & Qt::ImSurroundingText) || (queries & Qt::ImCursorPosition) || (queries & Qt::ImAnchorPosition)) { - QString text = event.value(Qt::ImSurroundingText).toString(); - int cursor = event.value(Qt::ImCursorPosition).toInt(); - int anchor = event.value(Qt::ImAnchorPosition).toInt(); - - // Make sure text is not too big - if (text.toUtf8().size() > 2048) { - int c = qAbs(cursor - anchor) <= 512 ? qMin(cursor, anchor) + qAbs(cursor - anchor) / 2: cursor; - - const int offset = c - qBound(0, c, 512 - qMin(text.size() - c, 256)); - text = text.mid(offset + c - 256, 512); - cursor -= offset; - anchor -= offset; - } - - set_surrounding_text(text, QWaylandInputMethodEventBuilder::indexToWayland(text, cursor), QWaylandInputMethodEventBuilder::indexToWayland(text, anchor)); - } - - if (queries & Qt::ImHints) { - QWaylandInputMethodContentType contentType = QWaylandInputMethodContentType::convert(static_cast<Qt::InputMethodHints>(event.value(Qt::ImHints).toInt())); - set_content_type(contentType.hint, contentType.purpose); - } - - if (queries & Qt::ImCursorRectangle) { - const QRect &cRect = event.value(Qt::ImCursorRectangle).toRect(); - const QRect &windowRect = QGuiApplication::inputMethod()->inputItemTransform().mapRect(cRect); - const QMargins margins = window->frameMargins(); - const QRect &surfaceRect = windowRect.translated(margins.left(), margins.top()); - set_cursor_rectangle(surfaceRect.x(), surfaceRect.y(), surfaceRect.width(), surfaceRect.height()); - } - - if (queries & Qt::ImPreferredLanguage) { - const QString &language = event.value(Qt::ImPreferredLanguage).toString(); - set_preferred_language(language); - } - - update_state(m_serial, flags); - if (flags != update_state_change) { - if (m_resetCallback) - wl_callback_destroy(m_resetCallback); - m_resetCallback = wl_display_sync(m_display->wl_display()); - wl_callback_add_listener(m_resetCallback, &QWaylandTextInput::callbackListener, this); - } -} - -void QWaylandTextInput::setCursorInsidePreedit(int) -{ - // Not supported yet -} - -bool QWaylandTextInput::isInputPanelVisible() const -{ - return m_inputPanelVisible; -} - -QRectF QWaylandTextInput::keyboardRect() const -{ - return m_keyboardRectangle; -} - -QLocale QWaylandTextInput::locale() const -{ - return m_locale; -} - -Qt::LayoutDirection QWaylandTextInput::inputDirection() const -{ - return m_inputDirection; -} - -void QWaylandTextInput::zwp_text_input_v2_enter(uint32_t serial, ::wl_surface *surface) -{ - m_serial = serial; - m_surface = surface; - - updateState(Qt::ImQueryAll, update_state_enter); -} - -void QWaylandTextInput::zwp_text_input_v2_leave(uint32_t serial, ::wl_surface *surface) -{ - m_serial = serial; - - if (m_surface != surface) { - qCDebug(qLcQpaInputMethods()) << Q_FUNC_INFO << "Got leave event for surface" << surface << "focused surface" << m_surface; - } - - m_surface = nullptr; -} - -void QWaylandTextInput::zwp_text_input_v2_modifiers_map(wl_array *map) -{ - const QList<QByteArray> modifiersMap = QByteArray::fromRawData(static_cast<const char*>(map->data), map->size).split('\0'); - - m_modifiersMap.clear(); - - for (const QByteArray &modifier : modifiersMap) { - if (modifier == "Shift") - m_modifiersMap.append(Qt::ShiftModifier); - else if (modifier == "Control") - m_modifiersMap.append(Qt::ControlModifier); - else if (modifier == "Alt") - m_modifiersMap.append(Qt::AltModifier); - else if (modifier == "Mod1") - m_modifiersMap.append(Qt::AltModifier); - else if (modifier == "Mod4") - m_modifiersMap.append(Qt::MetaModifier); - else - m_modifiersMap.append(Qt::NoModifier); - } -} - -void QWaylandTextInput::zwp_text_input_v2_input_panel_state(uint32_t visible, int32_t x, int32_t y, int32_t width, int32_t height) -{ - const bool inputPanelVisible = (visible == input_panel_visibility_visible); - if (m_inputPanelVisible != inputPanelVisible) { - m_inputPanelVisible = inputPanelVisible; - QGuiApplicationPrivate::platformIntegration()->inputContext()->emitInputPanelVisibleChanged(); - } - const QRectF keyboardRectangle(x, y, width, height); - if (m_keyboardRectangle != keyboardRectangle) { - m_keyboardRectangle = keyboardRectangle; - QGuiApplicationPrivate::platformIntegration()->inputContext()->emitKeyboardRectChanged(); - } -} - -void QWaylandTextInput::zwp_text_input_v2_preedit_string(const QString &text, const QString &commit) -{ - if (m_resetCallback) { - qCDebug(qLcQpaInputMethods()) << "discard preedit_string: reset not confirmed"; - m_builder.reset(); - return; - } - - if (!QGuiApplication::focusObject()) - return; - - QInputMethodEvent event = m_builder.buildPreedit(text); - - m_builder.reset(); - m_preeditCommit = commit; - - QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event); -} - -void QWaylandTextInput::zwp_text_input_v2_preedit_styling(uint32_t index, uint32_t length, uint32_t style) -{ - m_builder.addPreeditStyling(index, length, style); -} - -void QWaylandTextInput::zwp_text_input_v2_preedit_cursor(int32_t index) -{ - m_builder.setPreeditCursor(index); -} - -void QWaylandTextInput::zwp_text_input_v2_commit_string(const QString &text) -{ - if (m_resetCallback) { - qCDebug(qLcQpaInputMethods()) << "discard commit_string: reset not confirmed"; - m_builder.reset(); - return; - } - - if (!QGuiApplication::focusObject()) - return; - - QInputMethodEvent event = m_builder.buildCommit(text); - - m_builder.reset(); - - QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event); -} - -void QWaylandTextInput::zwp_text_input_v2_cursor_position(int32_t index, int32_t anchor) -{ - m_builder.setCursorPosition(index, anchor); -} - -void QWaylandTextInput::zwp_text_input_v2_delete_surrounding_text(uint32_t before_length, uint32_t after_length) -{ - m_builder.setDeleteSurroundingText(before_length, after_length); -} - -void QWaylandTextInput::zwp_text_input_v2_keysym(uint32_t time, uint32_t sym, uint32_t state, uint32_t modifiers) -{ #if QT_CONFIG(xkbcommon) - if (m_resetCallback) { - qCDebug(qLcQpaInputMethods()) << "discard keysym: reset not confirmed"; - return; - } - - if (!QGuiApplication::focusWindow()) - return; - - Qt::KeyboardModifiers qtModifiers = modifiersToQtModifiers(modifiers); - - QEvent::Type type = state == WL_KEYBOARD_KEY_STATE_PRESSED ? QEvent::KeyPress : QEvent::KeyRelease; - QString text = QXkbCommon::lookupStringNoKeysymTransformations(sym); - int qtkey = QXkbCommon::keysymToQtKey(sym, qtModifiers); - - QWindowSystemInterface::handleKeyEvent(QGuiApplication::focusWindow(), - time, type, qtkey, qtModifiers, text); -#else - Q_UNUSED(time); - Q_UNUSED(sym); - Q_UNUSED(state); - Q_UNUSED(modifiers); +#include <locale.h> #endif -} - -void QWaylandTextInput::zwp_text_input_v2_language(const QString &language) -{ - if (m_resetCallback) { - qCDebug(qLcQpaInputMethods()) << "discard language: reset not confirmed"; - return; - } - - const QLocale locale(language); - if (m_locale != locale) { - m_locale = locale; - QGuiApplicationPrivate::platformIntegration()->inputContext()->emitLocaleChanged(); - } -} - -void QWaylandTextInput::zwp_text_input_v2_text_direction(uint32_t direction) -{ - if (m_resetCallback) { - qCDebug(qLcQpaInputMethods()) << "discard text_direction: reset not confirmed"; - return; - } - - const Qt::LayoutDirection inputDirection = (direction == text_direction_auto) ? Qt::LayoutDirectionAuto : - (direction == text_direction_ltr) ? Qt::LeftToRight : - (direction == text_direction_rtl) ? Qt::RightToLeft : Qt::LayoutDirectionAuto; - if (m_inputDirection != inputDirection) { - m_inputDirection = inputDirection; - QGuiApplicationPrivate::platformIntegration()->inputContext()->emitInputDirectionChanged(m_inputDirection); - } -} -void QWaylandTextInput::zwp_text_input_v2_input_method_changed(uint32_t serial, uint32_t flags) -{ - Q_UNUSED(flags); +QT_BEGIN_NAMESPACE - m_serial = serial; - updateState(Qt::ImQueryAll, update_state_full); -} +Q_LOGGING_CATEGORY(qLcQpaInputMethods, "qt.qpa.input.methods") -Qt::KeyboardModifiers QWaylandTextInput::modifiersToQtModifiers(uint32_t modifiers) -{ - Qt::KeyboardModifiers ret = Qt::NoModifier; - for (int i = 0; modifiers >>= 1; ++i) { - ret |= m_modifiersMap[i]; - } - return ret; -} +namespace QtWaylandClient { QWaylandInputContext::QWaylandInputContext(QWaylandDisplay *display) : mDisplay(display) @@ -400,29 +35,35 @@ QWaylandInputContext::~QWaylandInputContext() bool QWaylandInputContext::isValid() const { - return mDisplay->textInputManager() != nullptr; + return mDisplay->textInputManagerv2() != nullptr || mDisplay->textInputManagerv1() != nullptr || mDisplay->textInputManagerv3() != nullptr; } void QWaylandInputContext::reset() { qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO; +#if QT_CONFIG(xkbcommon) + if (m_composeState) + xkb_compose_state_reset(m_composeState); +#endif QPlatformInputContext::reset(); - if (!textInput()) + QWaylandTextInputInterface *inputInterface = textInput(); + if (!inputInterface) return; - textInput()->reset(); + inputInterface->reset(); } void QWaylandInputContext::commit() { qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO; - if (!textInput()) + QWaylandTextInputInterface *inputInterface = textInput(); + if (!inputInterface) return; - textInput()->commit(); + inputInterface->commit(); } static ::wl_surface *surfaceForWindow(QWindow *window) @@ -438,99 +79,113 @@ void QWaylandInputContext::update(Qt::InputMethodQueries queries) { qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO << queries; - if (!QGuiApplication::focusObject() || !textInput()) + QWaylandTextInputInterface *inputInterface = textInput(); + if (!QGuiApplication::focusObject() || !inputInterface) return; auto *currentSurface = surfaceForWindow(mCurrentWindow); if (currentSurface && !inputMethodAccepted()) { - textInput()->disable(currentSurface); + inputInterface->disableSurface(currentSurface); mCurrentWindow.clear(); } else if (!currentSurface && inputMethodAccepted()) { QWindow *window = QGuiApplication::focusWindow(); if (auto *focusSurface = surfaceForWindow(window)) { - textInput()->enable(focusSurface); + inputInterface->enableSurface(focusSurface); mCurrentWindow = window; } } - textInput()->updateState(queries, QtWayland::zwp_text_input_v2::update_state_change); + inputInterface->updateState(queries, QWaylandTextInputInterface::update_state_change); } void QWaylandInputContext::invokeAction(QInputMethod::Action action, int cursorPostion) { - if (!textInput()) + QWaylandTextInputInterface *inputInterface = textInput(); + if (!inputInterface) return; if (action == QInputMethod::Click) - textInput()->setCursorInsidePreedit(cursorPostion); + inputInterface->setCursorInsidePreedit(cursorPostion); } void QWaylandInputContext::showInputPanel() { qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO; - if (!textInput()) + QWaylandTextInputInterface *inputInterface = textInput(); + if (!inputInterface) return; - textInput()->show_input_panel(); + inputInterface->showInputPanel(); } void QWaylandInputContext::hideInputPanel() { qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO; - if (!textInput()) + QWaylandTextInputInterface *inputInterface = textInput(); + if (!inputInterface) return; - textInput()->hide_input_panel(); + inputInterface->hideInputPanel(); } bool QWaylandInputContext::isInputPanelVisible() const { qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO; - if (!textInput()) + QWaylandTextInputInterface *inputInterface = textInput(); + if (!inputInterface) return QPlatformInputContext::isInputPanelVisible(); - return textInput()->isInputPanelVisible(); + return inputInterface->isInputPanelVisible(); } QRectF QWaylandInputContext::keyboardRect() const { qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO; - if (!textInput()) + QWaylandTextInputInterface *inputInterface = textInput(); + if (!inputInterface) return QPlatformInputContext::keyboardRect(); - return textInput()->keyboardRect(); + return inputInterface->keyboardRect(); } QLocale QWaylandInputContext::locale() const { qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO; - if (!textInput()) + QWaylandTextInputInterface *inputInterface = textInput(); + if (!inputInterface) return QPlatformInputContext::locale(); - return textInput()->locale(); + return inputInterface->locale(); } Qt::LayoutDirection QWaylandInputContext::inputDirection() const { qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO; - if (!textInput()) + QWaylandTextInputInterface *inputInterface = textInput(); + if (!inputInterface) return QPlatformInputContext::inputDirection(); - return textInput()->inputDirection(); + return inputInterface->inputDirection(); } -void QWaylandInputContext::setFocusObject(QObject *) +void QWaylandInputContext::setFocusObject(QObject *object) { qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO; +#if QT_CONFIG(xkbcommon) + m_focusObject = object; +#else + Q_UNUSED(object); +#endif - if (!textInput()) + QWaylandTextInputInterface *inputInterface = textInput(); + if (!inputInterface) return; QWindow *window = QGuiApplication::focusWindow(); @@ -539,7 +194,7 @@ void QWaylandInputContext::setFocusObject(QObject *) if (mCurrentWindow.data() != window || !inputMethodAccepted()) { auto *surface = static_cast<QWaylandWindow *>(mCurrentWindow->handle())->wlSurface(); if (surface) - textInput()->disable(surface); + inputInterface->disableSurface(surface); mCurrentWindow.clear(); } } @@ -548,19 +203,104 @@ void QWaylandInputContext::setFocusObject(QObject *) if (mCurrentWindow.data() != window) { auto *surface = static_cast<QWaylandWindow *>(window->handle())->wlSurface(); if (surface) { - textInput()->enable(surface); + inputInterface->enableSurface(surface); mCurrentWindow = window; } } - textInput()->updateState(Qt::ImQueryAll, QtWayland::zwp_text_input_v2::update_state_enter); + inputInterface->updateState(Qt::ImQueryAll, QWaylandTextInputInterface::update_state_enter); + } +} + +QWaylandTextInputInterface *QWaylandInputContext::textInput() const +{ + return mDisplay->defaultInputDevice() ? mDisplay->defaultInputDevice()->textInput() : nullptr; +} + +#if QT_CONFIG(xkbcommon) + +void QWaylandInputContext::ensureInitialized() +{ + if (m_initialized) + return; + + if (!m_XkbContext) { + qCWarning(qLcQpaInputMethods) << "error: xkb context has not been set on" << metaObject()->className(); + return; + } + + m_initialized = true; + const char *const locale = setlocale(LC_CTYPE, nullptr); + qCDebug(qLcQpaInputMethods) << "detected locale (LC_CTYPE):" << locale; + + m_composeTable = xkb_compose_table_new_from_locale(m_XkbContext, locale, XKB_COMPOSE_COMPILE_NO_FLAGS); + if (m_composeTable) + m_composeState = xkb_compose_state_new(m_composeTable, XKB_COMPOSE_STATE_NO_FLAGS); + + if (!m_composeTable) { + qCWarning(qLcQpaInputMethods, "failed to create compose table"); + return; + } + if (!m_composeState) { + qCWarning(qLcQpaInputMethods, "failed to create compose state"); + return; } } -QWaylandTextInput *QWaylandInputContext::textInput() const +bool QWaylandInputContext::filterEvent(const QEvent *event) { - return mDisplay->defaultInputDevice()->textInput(); + auto keyEvent = static_cast<const QKeyEvent *>(event); + if (keyEvent->type() != QEvent::KeyPress) + return false; + + if (!inputMethodAccepted()) + return false; + + // lazy initialization - we don't want to do this on an app startup + ensureInitialized(); + + if (!m_composeTable || !m_composeState) + return false; + + xkb_compose_state_feed(m_composeState, keyEvent->nativeVirtualKey()); + + switch (xkb_compose_state_get_status(m_composeState)) { + case XKB_COMPOSE_COMPOSING: + return true; + case XKB_COMPOSE_CANCELLED: + reset(); + return false; + case XKB_COMPOSE_COMPOSED: + { + const int size = xkb_compose_state_get_utf8(m_composeState, nullptr, 0); + QVarLengthArray<char, 32> buffer(size + 1); + xkb_compose_state_get_utf8(m_composeState, buffer.data(), buffer.size()); + QString composedText = QString::fromUtf8(buffer.constData()); + + QInputMethodEvent event; + event.setCommitString(composedText); + + if (!m_focusObject && qApp) + m_focusObject = qApp->focusObject(); + + if (m_focusObject) + QCoreApplication::sendEvent(m_focusObject, &event); + else + qCWarning(qLcQpaInputMethods, "no focus object"); + + reset(); + return true; + } + case XKB_COMPOSE_NOTHING: + return false; + default: + Q_UNREACHABLE_RETURN(false); + } } +#endif + } QT_END_NAMESPACE + +#include "moc_qwaylandinputcontext_p.cpp" diff --git a/src/client/qwaylandinputcontext_p.h b/src/client/qwaylandinputcontext_p.h index 10132dfe1..e6ce21d34 100644 --- a/src/client/qwaylandinputcontext_p.h +++ b/src/client/qwaylandinputcontext_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWaylandClient module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDINPUTCONTEXT_H @@ -54,82 +18,23 @@ #include <qpa/qplatforminputcontext.h> -#include <QLoggingCategory> #include <QPointer> -#include <QRectF> -#include <QVector> -#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h> -#include <qwaylandinputmethodeventbuilder_p.h> +#include "qwaylandtextinputinterface_p.h" +#include <qtwaylandclientglobal_p.h> +#if QT_CONFIG(xkbcommon) +#include <xkbcommon/xkbcommon-compose.h> +#endif struct wl_callback; struct wl_callback_listener; QT_BEGIN_NAMESPACE -Q_DECLARE_LOGGING_CATEGORY(qLcQpaInputMethods) - namespace QtWaylandClient { class QWaylandDisplay; -class QWaylandTextInput : public QtWayland::zwp_text_input_v2 -{ -public: - QWaylandTextInput(QWaylandDisplay *display, struct ::zwp_text_input_v2 *text_input); - ~QWaylandTextInput() override; - - void reset(); - void commit(); - void updateState(Qt::InputMethodQueries queries, uint32_t flags); - - void setCursorInsidePreedit(int cursor); - - bool isInputPanelVisible() const; - QRectF keyboardRect() const; - - QLocale locale() const; - Qt::LayoutDirection inputDirection() const; - -protected: - void zwp_text_input_v2_enter(uint32_t serial, struct ::wl_surface *surface) override; - void zwp_text_input_v2_leave(uint32_t serial, struct ::wl_surface *surface) override; - void zwp_text_input_v2_modifiers_map(wl_array *map) override; - void zwp_text_input_v2_input_panel_state(uint32_t state, int32_t x, int32_t y, int32_t width, int32_t height) override; - void zwp_text_input_v2_preedit_string(const QString &text, const QString &commit) override; - void zwp_text_input_v2_preedit_styling(uint32_t index, uint32_t length, uint32_t style) override; - void zwp_text_input_v2_preedit_cursor(int32_t index) override; - void zwp_text_input_v2_commit_string(const QString &text) override; - void zwp_text_input_v2_cursor_position(int32_t index, int32_t anchor) override; - void zwp_text_input_v2_delete_surrounding_text(uint32_t before_length, uint32_t after_length) override; - void zwp_text_input_v2_keysym(uint32_t time, uint32_t sym, uint32_t state, uint32_t modifiers) override; - void zwp_text_input_v2_language(const QString &language) override; - void zwp_text_input_v2_text_direction(uint32_t direction) override; - void zwp_text_input_v2_input_method_changed(uint32_t serial, uint32_t flags) override; - -private: - Qt::KeyboardModifiers modifiersToQtModifiers(uint32_t modifiers); - - QWaylandDisplay *m_display = nullptr; - QWaylandInputMethodEventBuilder m_builder; - - QVector<Qt::KeyboardModifier> m_modifiersMap; - - uint32_t m_serial = 0; - struct ::wl_surface *m_surface = nullptr; - - QString m_preeditCommit; - - bool m_inputPanelVisible = false; - QRectF m_keyboardRectangle; - QLocale m_locale; - Qt::LayoutDirection m_inputDirection = Qt::LayoutDirectionAuto; - - struct ::wl_callback *m_resetCallback = nullptr; - static const wl_callback_listener callbackListener; - static void resetCallback(void *data, struct wl_callback *wl_callback, uint32_t time); -}; - class QWaylandInputContext : public QPlatformInputContext { Q_OBJECT @@ -155,11 +60,28 @@ public: void setFocusObject(QObject *object) override; +#if QT_CONFIG(xkbcommon) + bool filterEvent(const QEvent *event) override; + + // This invokable is called from QXkbCommon::setXkbContext(). + Q_INVOKABLE void setXkbContext(struct xkb_context *context) { m_XkbContext = context; } +#endif + private: - QWaylandTextInput *textInput() const; + QWaylandTextInputInterface *textInput() const; QWaylandDisplay *mDisplay = nullptr; QPointer<QWindow> mCurrentWindow; + +#if QT_CONFIG(xkbcommon) + void ensureInitialized(); + + bool m_initialized = false; + QObject *m_focusObject = nullptr; + xkb_compose_table *m_composeTable = nullptr; + xkb_compose_state *m_composeState = nullptr; + struct xkb_context *m_XkbContext = nullptr; +#endif }; } diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp index a9da452dc..ce04971ba 100644 --- a/src/client/qwaylandinputdevice.cpp +++ b/src/client/qwaylandinputdevice.cpp @@ -1,45 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandinputdevice_p.h" #include "qwaylandintegration_p.h" +#include "qwaylandtextinputv3_p.h" #include "qwaylandwindow_p.h" #include "qwaylandsurface_p.h" #include "qwaylandbuffer_p.h" @@ -50,17 +15,26 @@ #if QT_CONFIG(wayland_client_primary_selection) #include "qwaylandprimaryselectionv1_p.h" #endif +#if QT_CONFIG(tabletevent) +#include "qwaylandtabletv2_p.h" +#endif +#include "qwaylandpointergestures_p.h" #include "qwaylandtouch_p.h" #include "qwaylandscreen_p.h" #include "qwaylandcursor_p.h" #include "qwaylanddisplay_p.h" #include "qwaylandshmbackingstore_p.h" +#include "qwaylandtextinputv1_p.h" +#include "qwaylandtextinputv2_p.h" +#include "qwaylandtextinputinterface_p.h" #include "qwaylandinputcontext_p.h" +#include "qwaylandinputmethodcontext_p.h" #include <QtGui/private/qpixmap_raster_p.h> #include <QtGui/private/qguiapplication_p.h> #include <qpa/qplatformwindow.h> #include <qpa/qplatforminputcontext.h> +#include <qpa/qplatformtheme.h> #include <QDebug> #include <unistd.h> @@ -72,6 +46,7 @@ #endif #include <QtGui/QGuiApplication> +#include <QtGui/QPointingDevice> QT_BEGIN_NAMESPACE @@ -79,21 +54,27 @@ namespace QtWaylandClient { Q_LOGGING_CATEGORY(lcQpaWaylandInput, "qt.qpa.wayland.input"); +// The maximum number of concurrent touchpoints is not exposed in wayland, so we assume a +// reasonable number of them. As of 2021 most touchscreen panels support 10 concurrent touchpoints. +static const int MaxTouchPoints = 10; + QWaylandInputDevice::Keyboard::Keyboard(QWaylandInputDevice *p) : mParent(p) { - mRepeatTimer.callOnTimeout([&]() { + init(p->get_keyboard()); + mRepeatTimer.callOnTimeout(this, [&]() { if (!focusWindow()) { // We destroyed the keyboard focus surface, but the server didn't get the message yet... // or the server didn't send an enter event first. return; } - mRepeatTimer.setInterval(mRepeatRate); - handleKey(mRepeatKey.time, QEvent::KeyRelease, mRepeatKey.key, mRepeatKey.modifiers, - mRepeatKey.code, mRepeatKey.nativeVirtualKey, mRepeatKey.nativeModifiers, + mRepeatTimer.setInterval(1000 / mRepeatRate); + Qt::KeyboardModifiers modifiers = this->modifiers(); + handleKey(mRepeatKey.time, QEvent::KeyRelease, mRepeatKey.key, modifiers, + mRepeatKey.code, mRepeatKey.nativeVirtualKey, this->mNativeModifiers, mRepeatKey.text, true); - handleKey(mRepeatKey.time, QEvent::KeyPress, mRepeatKey.key, mRepeatKey.modifiers, - mRepeatKey.code, mRepeatKey.nativeVirtualKey, mRepeatKey.nativeModifiers, + handleKey(mRepeatKey.time, QEvent::KeyPress, mRepeatKey.key, modifiers, + mRepeatKey.code, mRepeatKey.nativeVirtualKey, this->mNativeModifiers, mRepeatKey.text, true); }); } @@ -128,8 +109,8 @@ bool QWaylandInputDevice::Keyboard::createDefaultKeymap() QWaylandInputDevice::Keyboard::~Keyboard() { if (mFocus) - QWindowSystemInterface::handleWindowActivated(nullptr); - if (mParent->mVersion >= 3) + QWindowSystemInterface::handleFocusWindowChanged(nullptr); + if (version() >= 3) wl_keyboard_release(object()); else wl_keyboard_destroy(object()); @@ -137,17 +118,28 @@ QWaylandInputDevice::Keyboard::~Keyboard() QWaylandWindow *QWaylandInputDevice::Keyboard::focusWindow() const { - return mFocus ? QWaylandWindow::fromWlSurface(mFocus) : nullptr; + return mFocus ? mFocus->waylandWindow() : nullptr; } QWaylandInputDevice::Pointer::Pointer(QWaylandInputDevice *seat) : mParent(seat) { + init(seat->get_pointer()); +#if QT_CONFIG(cursor) + if (auto cursorShapeManager = seat->mQDisplay->cursorShapeManager()) { + mCursor.shape.reset(new QWaylandCursorShape(cursorShapeManager->get_pointer(object()))); + } + + mCursor.frameTimer.setSingleShot(true); + mCursor.frameTimer.callOnTimeout(this, [&]() { + cursorTimerCallback(); + }); +#endif } QWaylandInputDevice::Pointer::~Pointer() { - if (mParent->mVersion >= 3) + if (version() >= 3) wl_pointer_release(object()); else wl_pointer_destroy(object()); @@ -162,23 +154,16 @@ QWaylandWindow *QWaylandInputDevice::Pointer::focusWindow() const class WlCallback : public QtWayland::wl_callback { public: - explicit WlCallback(::wl_callback *callback, std::function<void(uint32_t)> fn, bool autoDelete = false) + explicit WlCallback(::wl_callback *callback, std::function<void(uint32_t)> fn) : QtWayland::wl_callback(callback) , m_fn(fn) - , m_autoDelete(autoDelete) {} ~WlCallback() override { wl_callback_destroy(object()); } - bool done() const { return m_done; } void callback_done(uint32_t callback_data) override { - m_done = true; m_fn(callback_data); - if (m_autoDelete) - delete this; } private: - bool m_done = false; std::function<void(uint32_t)> m_fn; - bool m_autoDelete = false; }; class CursorSurface : public QWaylandSurface @@ -188,25 +173,21 @@ public: : QWaylandSurface(display) , m_pointer(pointer) { - //TODO: When we upgrade to libwayland 1.10, use wl_surface_get_version instead. - m_version = display->compositorVersion(); connect(this, &QWaylandSurface::screensChanged, m_pointer, &QWaylandInputDevice::Pointer::updateCursor); } - void hide() + void reset() { - uint serial = m_pointer->mEnterSerial; - Q_ASSERT(serial); - m_pointer->set_cursor(serial, nullptr, 0, 0); m_setSerial = 0; + m_hotspot = QPoint(); } // Size and hotspot are in surface coordinates void update(wl_buffer *buffer, const QPoint &hotspot, const QSize &size, int bufferScale, bool animated = false) { // Calling code needs to ensure buffer scale is supported if != 1 - Q_ASSERT(bufferScale == 1 || m_version >= 3); + Q_ASSERT(bufferScale == 1 || version() >= 3); auto enterSerial = m_pointer->mEnterSerial; if (m_setSerial < enterSerial || m_hotspot != hotspot) { @@ -215,7 +196,7 @@ public: m_hotspot = hotspot; } - if (m_version >= 3) + if (version() >= 3) set_buffer_scale(bufferScale); attach(buffer, 0, 0); @@ -224,7 +205,7 @@ public: if (animated) { m_frameCallback.reset(new WlCallback(frame(), [this](uint32_t time){ Q_UNUSED(time); - m_pointer->updateCursor(); + m_pointer->cursorFrameCallback(); })); } commit(); @@ -241,29 +222,15 @@ public: private: QScopedPointer<WlCallback> m_frameCallback; QWaylandInputDevice::Pointer *m_pointer = nullptr; - uint m_version = 0; uint m_setSerial = 0; QPoint m_hotspot; }; -QString QWaylandInputDevice::Pointer::cursorThemeName() const -{ - static QString themeName = qEnvironmentVariable("XCURSOR_THEME", QStringLiteral("default")); - return themeName; -} - -int QWaylandInputDevice::Pointer::cursorSize() const -{ - constexpr int defaultCursorSize = 32; - static const int xCursorSize = qEnvironmentVariableIntValue("XCURSOR_SIZE"); - return xCursorSize > 0 ? xCursorSize : defaultCursorSize; -} - int QWaylandInputDevice::Pointer::idealCursorScale() const { - // set_buffer_scale is not supported on earlier versions - if (seat()->mQDisplay->compositorVersion() < 3) + if (seat()->mQDisplay->compositor()->version() < 3) { return 1; + } if (auto *s = mCursor.surface.data()) { if (s->outputScale() > 0) @@ -275,17 +242,30 @@ int QWaylandInputDevice::Pointer::idealCursorScale() const void QWaylandInputDevice::Pointer::updateCursorTheme() { + QString cursorThemeName; + QSize cursorSize; + + if (const QPlatformTheme *platformTheme = QGuiApplicationPrivate::platformTheme()) { + cursorThemeName = platformTheme->themeHint(QPlatformTheme::MouseCursorTheme).toString(); + cursorSize = platformTheme->themeHint(QPlatformTheme::MouseCursorSize).toSize(); + } + + if (cursorThemeName.isEmpty()) + cursorThemeName = QStringLiteral("default"); + if (cursorSize.isEmpty()) + cursorSize = QSize(24, 24); + int scale = idealCursorScale(); - int pixelSize = cursorSize() * scale; + int pixelSize = cursorSize.width() * scale; auto *display = seat()->mQDisplay; - mCursor.theme = display->loadCursorTheme(cursorThemeName(), pixelSize); + mCursor.theme = display->loadCursorTheme(cursorThemeName, pixelSize); if (!mCursor.theme) return; // A warning has already been printed in loadCursorTheme - if (auto *arrow = mCursor.theme->cursorImage(Qt::ArrowCursor)) { - int arrowPixelSize = qMax(arrow->width, arrow->height); // Not all cursor themes are square - while (scale > 1 && arrowPixelSize / scale < cursorSize()) + if (auto *arrow = mCursor.theme->cursor(Qt::ArrowCursor)) { + int arrowPixelSize = qMax(arrow->images[0]->width, arrow->images[0]->height); // Not all cursor themes are square + while (scale > 1 && arrowPixelSize / scale < cursorSize.width()) --scale; } else { qCWarning(lcQpaWayland) << "Cursor theme does not support the arrow cursor"; @@ -302,7 +282,8 @@ void QWaylandInputDevice::Pointer::updateCursor() if (shape == Qt::BlankCursor) { if (mCursor.surface) - mCursor.surface->hide(); + mCursor.surface->reset(); + set_cursor(mEnterSerial, nullptr, 0, 0); return; } @@ -318,6 +299,14 @@ void QWaylandInputDevice::Pointer::updateCursor() return; } + if (mCursor.shape) { + if (mCursor.surface) { + mCursor.surface->reset(); + } + mCursor.shape->setShape(mEnterSerial, shape); + return; + } + if (!mCursor.theme || idealCursorScale() != mCursor.themeBufferScale) updateCursorTheme(); @@ -326,12 +315,26 @@ void QWaylandInputDevice::Pointer::updateCursor() // Set from shape using theme uint time = seat()->mCursor.animationTimer.elapsed(); - if (struct ::wl_cursor_image *image = mCursor.theme->cursorImage(shape, time)) { + + if (struct ::wl_cursor *waylandCursor = mCursor.theme->cursor(shape)) { + uint duration = 0; + int frame = wl_cursor_frame_and_duration(waylandCursor, time, &duration); + ::wl_cursor_image *image = waylandCursor->images[frame]; + struct wl_buffer *buffer = wl_cursor_image_get_buffer(image); + if (!buffer) { + qCWarning(lcQpaWayland) << "Could not find buffer for cursor" << shape; + return; + } int bufferScale = mCursor.themeBufferScale; QPoint hotspot = QPoint(image->hotspot_x, image->hotspot_y) / bufferScale; QSize size = QSize(image->width, image->height) / bufferScale; - bool animated = image->delay > 0; + bool animated = duration > 0; + if (animated) { + mCursor.gotFrameCallback = false; + mCursor.gotTimerCallback = false; + mCursor.frameTimer.start(duration); + } getOrCreateCursorSurface()->update(buffer, hotspot, size, bufferScale, animated); return; } @@ -346,26 +349,42 @@ CursorSurface *QWaylandInputDevice::Pointer::getOrCreateCursorSurface() return mCursor.surface.get(); } +void QWaylandInputDevice::Pointer::cursorTimerCallback() +{ + mCursor.gotTimerCallback = true; + if (mCursor.gotFrameCallback) { + updateCursor(); + } +} + +void QWaylandInputDevice::Pointer::cursorFrameCallback() +{ + mCursor.gotFrameCallback = true; + if (mCursor.gotTimerCallback) { + updateCursor(); + } +} + #endif // QT_CONFIG(cursor) QWaylandInputDevice::Touch::Touch(QWaylandInputDevice *p) : mParent(p) { + init(p->get_touch()); } QWaylandInputDevice::Touch::~Touch() { - if (mParent->mVersion >= 3) + if (version() >= 3) wl_touch_release(object()); else wl_touch_destroy(object()); } QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version, uint32_t id) - : QtWayland::wl_seat(display->wl_registry(), id, qMin(version, 5)) + : QtWayland::wl_seat(display->wl_registry(), id, qMin(version, 9)) , mQDisplay(display) , mDisplay(display->wl_display()) - , mVersion(qMin(version, 5)) { #if QT_CONFIG(wayland_datadevice) if (mQDisplay->dndSelectionHandler()) { @@ -379,16 +398,33 @@ QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version, setPrimarySelectionDevice(psm->createDevice(this)); #endif - if (mQDisplay->textInputManager()) - mTextInput.reset(new QWaylandTextInput(mQDisplay, mQDisplay->textInputManager()->get_text_input(wl_seat()))); + if (mQDisplay->textInputManagerv1()) { + auto textInput = new QWaylandTextInputv1(mQDisplay, mQDisplay->textInputManagerv1()->create_text_input()); + textInput->setSeat(wl_seat()); + mTextInput.reset(textInput); + } + + if (mQDisplay->textInputManagerv2()) + mTextInput.reset(new QWaylandTextInputv2(mQDisplay, mQDisplay->textInputManagerv2()->get_text_input(wl_seat()))); + if (mQDisplay->textInputManagerv3()) + mTextInput.reset(new QWaylandTextInputv3(mQDisplay, mQDisplay->textInputManagerv3()->get_text_input(wl_seat()))); + + if (mQDisplay->textInputMethodManager()) + mTextInputMethod.reset(new QWaylandTextInputMethod(mQDisplay, mQDisplay->textInputMethodManager()->get_text_input_method(wl_seat()))); + +#if QT_CONFIG(tabletevent) + if (auto *tm = mQDisplay->tabletManager()) + mTabletSeat.reset(new QWaylandTabletSeatV2(tm, this)); +#endif } QWaylandInputDevice::~QWaylandInputDevice() { - delete mPointer; - delete mKeyboard; - delete mTouch; + if (version() >= WL_SEAT_RELEASE_SINCE_VERSION) + release(); + else + wl_seat_destroy(object()); } void QWaylandInputDevice::seat_capabilities(uint32_t caps) @@ -396,37 +432,54 @@ void QWaylandInputDevice::seat_capabilities(uint32_t caps) mCaps = caps; if (caps & WL_SEAT_CAPABILITY_KEYBOARD && !mKeyboard) { - mKeyboard = createKeyboard(this); - mKeyboard->init(get_keyboard()); + mKeyboard.reset(createKeyboard(this)); } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && mKeyboard) { - delete mKeyboard; - mKeyboard = nullptr; + mKeyboard.reset(); } if (caps & WL_SEAT_CAPABILITY_POINTER && !mPointer) { - mPointer = createPointer(this); - mPointer->init(get_pointer()); + mPointer.reset(createPointer(this)); + + auto *pointerGestures = mQDisplay->pointerGestures(); + if (pointerGestures) { + // NOTE: The name of the device and its system ID are not exposed on Wayland. + mTouchPadDevice = new QPointingDevice( + QLatin1String("touchpad"), 0, QInputDevice::DeviceType::TouchPad, + QPointingDevice::PointerType::Finger, QInputDevice::Capability::Position, + MaxTouchPoints, 0, QString(), QPointingDeviceUniqueId(), this); + QWindowSystemInterface::registerInputDevice(mTouchPadDevice); + mPointerGesturePinch.reset(pointerGestures->createPointerGesturePinch(this)); + mPointerGesturePinch->init(pointerGestures->get_pinch_gesture(mPointer->object())); + mPointerGestureSwipe.reset(pointerGestures->createPointerGestureSwipe(this)); + mPointerGestureSwipe->init(pointerGestures->get_swipe_gesture(mPointer->object())); + } } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && mPointer) { - delete mPointer; - mPointer = nullptr; + mPointer.reset(); + mPointerGesturePinch.reset(); + mPointerGestureSwipe.reset(); } if (caps & WL_SEAT_CAPABILITY_TOUCH && !mTouch) { - mTouch = createTouch(this); - mTouch->init(get_touch()); + mTouch.reset(createTouch(this)); if (!mTouchDevice) { - mTouchDevice = new QTouchDevice; - mTouchDevice->setType(QTouchDevice::TouchScreen); - mTouchDevice->setCapabilities(QTouchDevice::Position); - QWindowSystemInterface::registerTouchDevice(mTouchDevice); + // TODO number of touchpoints, actual name and ID + mTouchDevice = new QPointingDevice( + QLatin1String("some touchscreen"), 0, QInputDevice::DeviceType::TouchScreen, + QPointingDevice::PointerType::Finger, QInputDevice::Capability::Position, + MaxTouchPoints, 0,QString(), QPointingDeviceUniqueId(), this); + QWindowSystemInterface::registerInputDevice(mTouchDevice); } } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && mTouch) { - delete mTouch; - mTouch = nullptr; + mTouch.reset(); } } +void QWaylandInputDevice::seat_name(const QString &name) +{ + mSeatName = name; +} + QWaylandInputDevice::Keyboard *QWaylandInputDevice::createKeyboard(QWaylandInputDevice *device) { return new Keyboard(device); @@ -444,17 +497,27 @@ QWaylandInputDevice::Touch *QWaylandInputDevice::createTouch(QWaylandInputDevice QWaylandInputDevice::Keyboard *QWaylandInputDevice::keyboard() const { - return mKeyboard; + return mKeyboard.data(); } QWaylandInputDevice::Pointer *QWaylandInputDevice::pointer() const { - return mPointer; + return mPointer.data(); +} + +QWaylandPointerGestureSwipe *QWaylandInputDevice::pointerGestureSwipe() const +{ + return mPointerGestureSwipe.data(); +} + +QWaylandPointerGesturePinch *QWaylandInputDevice::pointerGesturePinch() const +{ + return mPointerGesturePinch.data(); } QWaylandInputDevice::Touch *QWaylandInputDevice::touch() const { - return mTouch; + return mTouch.data(); } void QWaylandInputDevice::handleEndDrag() @@ -489,16 +552,26 @@ QWaylandPrimarySelectionDeviceV1 *QWaylandInputDevice::primarySelectionDevice() } #endif -void QWaylandInputDevice::setTextInput(QWaylandTextInput *textInput) +void QWaylandInputDevice::setTextInput(QWaylandTextInputInterface *textInput) { mTextInput.reset(textInput); } -QWaylandTextInput *QWaylandInputDevice::textInput() const +void QWaylandInputDevice::setTextInputMethod(QWaylandTextInputMethod *textInputMethod) +{ + mTextInputMethod.reset(textInputMethod); +} + +QWaylandTextInputInterface *QWaylandInputDevice::textInput() const { return mTextInput.data(); } +QWaylandTextInputMethod *QWaylandInputDevice::textInputMethod() const +{ + return mTextInputMethod.data(); +} + void QWaylandInputDevice::removeMouseButtonFromState(Qt::MouseButton button) { if (mPointer) @@ -594,8 +667,8 @@ class EnterEvent : public QWaylandPointerEvent { public: EnterEvent(QWaylandWindow *surface, const QPointF &local, const QPointF &global) - : QWaylandPointerEvent(QWaylandPointerEvent::Enter, Qt::NoScrollPhase, surface, 0, - local, global, nullptr, Qt::NoModifier) + : QWaylandPointerEvent(QEvent::Enter, Qt::NoScrollPhase, surface, 0, + local, global, Qt::NoButton, Qt::NoButton, Qt::NoModifier) {} }; @@ -620,7 +693,7 @@ void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surf connect(mFocus.data(), &QObject::destroyed, this, &Pointer::handleFocusDestroyed); mSurfacePos = QPointF(wl_fixed_to_double(sx), wl_fixed_to_double(sy)); - mGlobalPos = window->window()->mapToGlobal(mSurfacePos.toPoint()); + mGlobalPos = window->mapToGlobal(mSurfacePos.toPoint()); mParent->mSerial = serial; mEnterSerial = serial; @@ -639,13 +712,18 @@ class LeaveEvent : public QWaylandPointerEvent { public: LeaveEvent(QWaylandWindow *surface, const QPointF &localPos, const QPointF &globalPos) - : QWaylandPointerEvent(QWaylandPointerEvent::Leave, Qt::NoScrollPhase, surface, 0, - localPos, globalPos, nullptr, Qt::NoModifier) + : QWaylandPointerEvent(QEvent::Leave, Qt::NoScrollPhase, surface, 0, + localPos, globalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier) {} }; void QWaylandInputDevice::Pointer::pointer_leave(uint32_t time, struct wl_surface *surface) { + invalidateFocus(); + mButtons = Qt::NoButton; + + mParent->mTime = time; + // The event may arrive after destroying the window, indicated by // a null surface. if (!surface) @@ -657,11 +735,6 @@ void QWaylandInputDevice::Pointer::pointer_leave(uint32_t time, struct wl_surfac if (!QWaylandWindow::mouseGrab()) setFrameEvent(new LeaveEvent(window, mSurfacePos, mGlobalPos)); - - invalidateFocus(); - mButtons = Qt::NoButton; - - mParent->mTime = time; } class MotionEvent : public QWaylandPointerEvent @@ -669,8 +742,8 @@ class MotionEvent : public QWaylandPointerEvent public: MotionEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos, const QPointF &globalPos, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers) - : QWaylandPointerEvent(QWaylandPointerEvent::Motion, Qt::NoScrollPhase, surface, - timestamp, localPos, globalPos, buttons, modifiers) + : QWaylandPointerEvent(QEvent::MouseMove, Qt::NoScrollPhase, surface, + timestamp, localPos, globalPos, buttons, Qt::NoButton, modifiers) { } }; @@ -686,7 +759,7 @@ void QWaylandInputDevice::Pointer::pointer_motion(uint32_t time, wl_fixed_t surf QPointF pos(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)); QPointF delta = pos - pos.toPoint(); - QPointF global = window->window()->mapToGlobal(pos.toPoint()); + QPointF global = window->mapToGlobal(pos.toPoint()); global += delta; mSurfacePos = pos; @@ -698,7 +771,7 @@ void QWaylandInputDevice::Pointer::pointer_motion(uint32_t time, wl_fixed_t surf // We can't know the true position since we're getting events for another surface, // so we just set it outside of the window boundaries. pos = QPointF(-1, -1); - global = grab->window()->mapToGlobal(pos.toPoint()); + global = grab->mapToGlobal(pos.toPoint()); window = grab; } setFrameEvent(new MotionEvent(window, time, pos, global, mButtons, mParent->modifiers())); @@ -708,9 +781,10 @@ class PressEvent : public QWaylandPointerEvent { public: PressEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos, - const QPointF &globalPos, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers) - : QWaylandPointerEvent(QWaylandPointerEvent::Press, Qt::NoScrollPhase, surface, - timestamp, localPos, globalPos, buttons, modifiers) + const QPointF &globalPos, Qt::MouseButtons buttons, Qt::MouseButton button, + Qt::KeyboardModifiers modifiers) + : QWaylandPointerEvent(QEvent::MouseButtonPress, Qt::NoScrollPhase, surface, + timestamp, localPos, globalPos, buttons, button, modifiers) { } }; @@ -719,9 +793,10 @@ class ReleaseEvent : public QWaylandPointerEvent { public: ReleaseEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos, - const QPointF &globalPos, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers) - : QWaylandPointerEvent(QWaylandPointerEvent::Release, Qt::NoScrollPhase, surface, - timestamp, localPos, globalPos, buttons, modifiers) + const QPointF &globalPos, Qt::MouseButtons buttons, Qt::MouseButton button, + Qt::KeyboardModifiers modifiers) + : QWaylandPointerEvent(QEvent::MouseButtonRelease, Qt::NoScrollPhase, surface, + timestamp, localPos, globalPos, buttons, button, modifiers) { } }; @@ -760,6 +835,8 @@ void QWaylandInputDevice::Pointer::pointer_button(uint32_t serial, uint32_t time default: return; // invalid button number (as far as Qt is concerned) } + mLastButton = qt_button; + if (state) mButtons |= qt_button; else @@ -776,15 +853,15 @@ void QWaylandInputDevice::Pointer::pointer_button(uint32_t serial, uint32_t time QPointF global = mGlobalPos; if (grab && grab != focusWindow()) { pos = QPointF(-1, -1); - global = grab->window()->mapToGlobal(pos.toPoint()); + global = grab->mapToGlobal(pos.toPoint()); window = grab; } if (state) - setFrameEvent(new PressEvent(window, time, pos, global, mButtons, mParent->modifiers())); + setFrameEvent(new PressEvent(window, time, pos, global, mButtons, qt_button, mParent->modifiers())); else - setFrameEvent(new ReleaseEvent(window, time, pos, global, mButtons, mParent->modifiers())); + setFrameEvent(new ReleaseEvent(window, time, pos, global, mButtons, qt_button, mParent->modifiers())); } void QWaylandInputDevice::Pointer::invalidateFocus() @@ -798,10 +875,13 @@ void QWaylandInputDevice::Pointer::invalidateFocus() void QWaylandInputDevice::Pointer::releaseButtons() { + if (mButtons == Qt::NoButton) + return; + mButtons = Qt::NoButton; if (auto *window = focusWindow()) { - MotionEvent e(focusWindow(), mParent->mTime, mSurfacePos, mGlobalPos, mButtons, mParent->modifiers()); + ReleaseEvent e(focusWindow(), mParent->mTime, mSurfacePos, mGlobalPos, mButtons, mLastButton, mParent->modifiers()); window->handleMouse(mParent, e); } } @@ -811,9 +891,11 @@ class WheelEvent : public QWaylandPointerEvent public: WheelEvent(QWaylandWindow *surface, Qt::ScrollPhase phase, ulong timestamp, const QPointF &local, const QPointF &global, const QPoint &pixelDelta, const QPoint &angleDelta, - Qt::MouseEventSource source, Qt::KeyboardModifiers modifiers) - : QWaylandPointerEvent(QWaylandPointerEvent::Wheel, phase, surface, timestamp, - local, global, pixelDelta, angleDelta, source, modifiers) + Qt::MouseEventSource source, Qt::KeyboardModifiers modifiers, bool inverted) + : QWaylandPointerEvent(QEvent::Wheel, phase, surface, timestamp, local, global, + modifiers & Qt::AltModifier ? pixelDelta.transposed() : pixelDelta, + modifiers & Qt::AltModifier ? angleDelta.transposed() : angleDelta, + source, modifiers, inverted) { } }; @@ -844,7 +926,7 @@ void QWaylandInputDevice::Pointer::pointer_axis(uint32_t time, uint32_t axis, in mParent->mTime = time; - if (mParent->mVersion < WL_POINTER_FRAME_SINCE_VERSION) { + if (version() < WL_POINTER_FRAME_SINCE_VERSION) { qCDebug(lcQpaWaylandInput) << "Flushing new event; no frame event in this version"; flushFrameEvent(); } @@ -867,6 +949,8 @@ void QWaylandInputDevice::Pointer::pointer_axis_source(uint32_t source) case axis_source_continuous: qCDebug(lcQpaWaylandInput) << "Axis source continuous"; break; + case axis_source_wheel_tilt: + qCDebug(lcQpaWaylandInput) << "Axis source wheel tilt"; } mFrameData.axisSource = axis_source(source); } @@ -905,8 +989,9 @@ void QWaylandInputDevice::Pointer::pointer_axis_stop(uint32_t time, uint32_t axi if (!target) target = focusWindow(); Qt::KeyboardModifiers mods = mParent->modifiers(); + const bool inverted = mFrameData.verticalAxisInverted || mFrameData.horizontalAxisInverted; WheelEvent wheelEvent(focusWindow(), Qt::ScrollEnd, mParent->mTime, mSurfacePos, mGlobalPos, - QPoint(), QPoint(), Qt::MouseEventNotSynthesized, mods); + QPoint(), QPoint(), Qt::MouseEventNotSynthesized, mods, inverted); target->handleMouse(mParent, wheelEvent); mScrollBeginSent = false; mScrollDeltaRemainder = QPointF(); @@ -917,14 +1002,16 @@ void QWaylandInputDevice::Pointer::pointer_axis_discrete(uint32_t axis, int32_t if (!focusWindow()) return; + const int32_t delta120 = value * 15 * 8; + switch (axis) { case axis_vertical_scroll: qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_discrete vertical:" << value; - mFrameData.discreteDelta.ry() += value; + mFrameData.delta120.ry() += delta120; break; case axis_horizontal_scroll: qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_discrete horizontal:" << value; - mFrameData.discreteDelta.rx() += value; + mFrameData.delta120.rx() += delta120; break; default: //TODO: is this really needed? @@ -933,6 +1020,41 @@ void QWaylandInputDevice::Pointer::pointer_axis_discrete(uint32_t axis, int32_t } } +void QWaylandInputDevice::Pointer::pointer_axis_value120(uint32_t axis, int32_t value) +{ + if (!focusWindow()) + return; + + switch (axis) { + case axis_vertical_scroll: + qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_value120 vertical:" << value; + mFrameData.delta120.ry() += value; + break; + case axis_horizontal_scroll: + qCDebug(lcQpaWaylandInput) << "wl_pointer.axis_value120 horizontal:" << value; + mFrameData.delta120.rx() += value; + break; + default: + qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_value120: Unknown axis:" << axis; + return; + } +} + +void QWaylandInputDevice::Pointer::pointer_axis_relative_direction(uint32_t axis, uint32_t direction) +{ + const bool inverted = direction == axis_relative_direction_inverted; + switch (axis) { + case axis_vertical_scroll: + mFrameData.verticalAxisInverted = inverted; + break; + case axis_horizontal_scroll: + mFrameData.horizontalAxisInverted = inverted; + break; + default: + qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_relative_direction: Unknown axis:" << axis; + } +} + void QWaylandInputDevice::Pointer::setFrameEvent(QWaylandPointerEvent *event) { qCDebug(lcQpaWaylandInput) << "Setting frame event " << event->type; @@ -943,7 +1065,7 @@ void QWaylandInputDevice::Pointer::setFrameEvent(QWaylandPointerEvent *event) mFrameData.event = event; - if (mParent->mVersion < WL_POINTER_FRAME_SINCE_VERSION) { + if (version() < WL_POINTER_FRAME_SINCE_VERSION) { qCDebug(lcQpaWaylandInput) << "Flushing new event; no frame event in this version"; flushFrameEvent(); } @@ -951,9 +1073,11 @@ void QWaylandInputDevice::Pointer::setFrameEvent(QWaylandPointerEvent *event) void QWaylandInputDevice::Pointer::FrameData::resetScrollData() { - discreteDelta = QPoint(); + delta120 = QPoint(); delta = QPointF(); axisSource = axis_source_wheel; + horizontalAxisInverted = false; + verticalAxisInverted = false; } bool QWaylandInputDevice::Pointer::FrameData::hasPixelDelta() const @@ -968,6 +1092,8 @@ bool QWaylandInputDevice::Pointer::FrameData::hasPixelDelta() const case axis_source_finger: case axis_source_continuous: return !delta.isNull(); + default: + return false; } } @@ -982,20 +1108,27 @@ QPoint QWaylandInputDevice::Pointer::FrameData::pixelDeltaAndError(QPointF *accu *accumulatedError += delta - pixelDelta; Q_ASSERT(qAbs(accumulatedError->x()) < 1.0); Q_ASSERT(qAbs(accumulatedError->y()) < 1.0); + + // for continuous scroll events things should be + // in the same direction + // i.e converted so downwards surface co-ordinates (positive axis_value) + // goes to downwards in wheel event (negative value) + pixelDelta *= -1; return pixelDelta; } QPoint QWaylandInputDevice::Pointer::FrameData::angleDelta() const { - if (discreteDelta.isNull()) { + if (delta120.isNull()) { // If we didn't get any discrete events, then we need to fall back to // the continuous information. return (delta * -12).toPoint(); //TODO: why multiply by 12? } // The angle delta is in eights of degrees, and our docs says most mice have - // 1 click = 15 degrees. It's also in the opposite direction of surface space. - return -discreteDelta * 15 * 8; + // 1 click = 15 degrees, i.e. 120 is one click. It's also in the opposite + // direction of surface space. + return -delta120; } Qt::MouseEventSource QWaylandInputDevice::Pointer::FrameData::wheelEventSource() const @@ -1026,7 +1159,7 @@ void QWaylandInputDevice::Pointer::flushScrollEvent() target->handleMouse(mParent, WheelEvent(focusWindow(), Qt::ScrollBegin, mParent->mTime, mSurfacePos, mGlobalPos, QPoint(), QPoint(), Qt::MouseEventNotSynthesized, - mParent->modifiers())); + mParent->modifiers(), false)); mScrollBeginSent = true; mScrollDeltaRemainder = QPointF(); } @@ -1035,11 +1168,15 @@ void QWaylandInputDevice::Pointer::flushScrollEvent() QPoint pixelDelta = mFrameData.pixelDeltaAndError(&mScrollDeltaRemainder); Qt::MouseEventSource source = mFrameData.wheelEventSource(); + + // The wayland protocol has separate horizontal and vertical axes, Qt has just the one inverted flag + // Pragmatically it should't come up + const bool inverted = mFrameData.verticalAxisInverted || mFrameData.horizontalAxisInverted; + qCDebug(lcQpaWaylandInput) << "Flushing scroll event" << phase << pixelDelta << angleDelta; target->handleMouse(mParent, WheelEvent(focusWindow(), phase, mParent->mTime, mSurfacePos, mGlobalPos, - pixelDelta, angleDelta, source, mParent->modifiers())); + pixelDelta, angleDelta, source, mParent->modifiers(), inverted)); } - mFrameData.resetScrollData(); } @@ -1048,11 +1185,13 @@ void QWaylandInputDevice::Pointer::flushFrameEvent() if (auto *event = mFrameData.event) { if (auto window = event->surface) { window->handleMouse(mParent, *event); - } else if (mFrameData.event->type == QWaylandPointerEvent::Type::Release) { + } else if (mFrameData.event->type == QEvent::MouseButtonRelease) { // If the window has been destroyed, we still need to report an up event, but it can't // be handled by the destroyed window (obviously), so send the event here instead. QWindowSystemInterface::handleMouseEvent(nullptr, event->timestamp, event->local, - event->global, event->buttons, event->modifiers); + event->global, event->buttons, + event->button, event->type, + event->modifiers);// , Qt::MouseEventSource source = Qt::MouseEventNotSynthesized); } delete mFrameData.event; mFrameData.event = nullptr; @@ -1077,7 +1216,7 @@ void QWaylandInputDevice::Keyboard::keyboard_keymap(uint32_t format, int32_t fd, return; } - char *map_str = static_cast<char *>(mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0)); + char *map_str = static_cast<char *>(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0)); if (map_str == MAP_FAILED) { close(fd); return; @@ -1112,13 +1251,17 @@ void QWaylandInputDevice::Keyboard::keyboard_enter(uint32_t time, struct wl_surf return; } + QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface); + if (!window) + return; + if (mFocus) { qCWarning(lcQpaWayland()) << "Unexpected wl_keyboard.enter event. Keyboard already has focus"; - disconnect(focusWindow(), &QWaylandWindow::wlSurfaceDestroyed, this, &Keyboard::handleFocusDestroyed); + disconnect(mFocus, &QWaylandSurface::destroyed, this, &Keyboard::handleFocusDestroyed); } - mFocus = surface; - connect(focusWindow(), &QWaylandWindow::wlSurfaceDestroyed, this, &Keyboard::handleFocusDestroyed); + mFocus = window->waylandSurface(); + connect(mFocus, &QWaylandSurface::destroyed, this, &Keyboard::handleFocusDestroyed); mParent->mQDisplay->handleKeyboardFocusChanged(mParent); } @@ -1132,13 +1275,17 @@ void QWaylandInputDevice::Keyboard::keyboard_leave(uint32_t time, struct wl_surf return; } - if (surface != mFocus) { + QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface); + if (!window) + return; + + if (window->waylandSurface() != mFocus) { qCWarning(lcQpaWayland) << "Ignoring unexpected wl_keyboard.leave event." << "wl_surface argument does not match the current focus" << "This is most likely a compositor bug"; return; } - disconnect(focusWindow(), &QWaylandWindow::wlSurfaceDestroyed, this, &Keyboard::handleFocusDestroyed); + disconnect(mFocus, &QWaylandSurface::destroyed, this, &Keyboard::handleFocusDestroyed); handleFocusLost(); } @@ -1150,7 +1297,7 @@ void QWaylandInputDevice::Keyboard::handleKey(ulong timestamp, QEvent::Type type QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext(); bool filtered = false; - if (inputContext && !mParent->mQDisplay->usingInputContextFromCompositor()) { + if (inputContext) { QKeyEvent event(type, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorepeat, count); event.setTimestamp(timestamp); @@ -1202,10 +1349,9 @@ void QWaylandInputDevice::Keyboard::keyboard_key(uint32_t serial, uint32_t time, auto code = key + 8; // map to wl_keyboard::keymap_format::keymap_format_xkb_v1 xkb_keysym_t sym = xkb_state_key_get_one_sym(mXkbState.get(), code); + Qt::KeyboardModifiers modifiers = QXkbCommon::modifiers(mXkbState.get(), sym); - Qt::KeyboardModifiers modifiers = mParent->modifiers(); - - int qtkey = QXkbCommon::keysymToQtKey(sym, modifiers, mXkbState.get(), code); + int qtkey = keysymToQtKey(sym, modifiers, mXkbState.get(), code); QString text = QXkbCommon::lookupString(mXkbState.get(), code); QEvent::Type type = isDown ? QEvent::KeyPress : QEvent::KeyRelease; @@ -1216,8 +1362,6 @@ void QWaylandInputDevice::Keyboard::keyboard_key(uint32_t serial, uint32_t time, mRepeatKey.code = code; mRepeatKey.time = time; mRepeatKey.text = text; - mRepeatKey.modifiers = modifiers; - mRepeatKey.nativeModifiers = mNativeModifiers; mRepeatKey.nativeVirtualKey = sym; mRepeatTimer.setInterval(mRepeatDelay); mRepeatTimer.start(); @@ -1238,25 +1382,12 @@ void QWaylandInputDevice::Keyboard::keyboard_key(uint32_t serial, uint32_t time, void QWaylandInputDevice::Keyboard::handleFocusDestroyed() { - // The signal is emitted by QWaylandWindow, which is not necessarily destroyed along with the - // surface, so we still need to disconnect the signal - auto *window = qobject_cast<QWaylandWindow *>(sender()); - disconnect(window, &QWaylandWindow::wlSurfaceDestroyed, this, &Keyboard::handleFocusDestroyed); - Q_ASSERT(window->wlSurface() == mFocus); handleFocusLost(); } void QWaylandInputDevice::Keyboard::handleFocusLost() { mFocus = nullptr; -#if QT_CONFIG(clipboard) - if (auto *dataDevice = mParent->dataDevice()) - dataDevice->invalidateSelectionOffer(); -#endif -#if QT_CONFIG(wayland_client_primary_selection) - if (auto *device = mParent->primarySelectionDevice()) - device->invalidateSelectionOffer(); -#endif mParent->mQDisplay->handleKeyboardFocusChanged(mParent); mRepeatTimer.stop(); } @@ -1307,14 +1438,14 @@ void QWaylandInputDevice::Touch::touch_down(uint32_t serial, mFocus = window; mParent->mQDisplay->setLastInputDevice(mParent, serial, mFocus); QPointF position(wl_fixed_to_double(x), wl_fixed_to_double(y)); - mParent->handleTouchPoint(id, Qt::TouchPointPressed, position); + mParent->handleTouchPoint(id, QEventPoint::Pressed, position); } void QWaylandInputDevice::Touch::touch_up(uint32_t serial, uint32_t time, int32_t id) { Q_UNUSED(serial); - Q_UNUSED(time); - mParent->handleTouchPoint(id, Qt::TouchPointReleased); + mParent->mTime = time; + mParent->handleTouchPoint(id, QEventPoint::Released); if (allTouchPointsReleased()) { mFocus = nullptr; @@ -1332,9 +1463,9 @@ void QWaylandInputDevice::Touch::touch_up(uint32_t serial, uint32_t time, int32_ void QWaylandInputDevice::Touch::touch_motion(uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y) { - Q_UNUSED(time); QPointF position(wl_fixed_to_double(x), wl_fixed_to_double(y)); - mParent->handleTouchPoint(id, Qt::TouchPointMoved, position); + mParent->mTime = time; + mParent->handleTouchPoint(id, QEventPoint::Updated, position); } void QWaylandInputDevice::Touch::touch_cancel() @@ -1345,21 +1476,30 @@ void QWaylandInputDevice::Touch::touch_cancel() if (touchExt) touchExt->touchCanceled(); + mFocus = nullptr; QWindowSystemInterface::handleTouchCancelEvent(nullptr, mParent->mTouchDevice); } -void QWaylandInputDevice::handleTouchPoint(int id, Qt::TouchPointState state, const QPointF &surfacePosition) +void QWaylandInputDevice::handleTouchPoint(int id, QEventPoint::State state, const QPointF &surfacePosition) { auto end = mTouch->mPendingTouchPoints.end(); - auto it = std::find_if(mTouch->mPendingTouchPoints.begin(), end, [id](auto tp){ return tp.id == id; }); + auto it = std::find_if(mTouch->mPendingTouchPoints.begin(), end, [id](const QWindowSystemInterface::TouchPoint &tp){ return tp.id == id; }); if (it == end) { it = mTouch->mPendingTouchPoints.insert(end, QWindowSystemInterface::TouchPoint()); it->id = id; } + // If the touch points were up and down in same frame, send out frame right away + else if ((it->state == QEventPoint::Pressed && state == QEventPoint::Released) + || (it->state == QEventPoint::Released && state == QEventPoint::Pressed)) { + mTouch->touch_frame(); + it = mTouch->mPendingTouchPoints.insert(mTouch->mPendingTouchPoints.end(), QWindowSystemInterface::TouchPoint()); + it->id = id; + } + QWindowSystemInterface::TouchPoint &tp = *it; // Only moved and pressed needs to update/set position - if (state == Qt::TouchPointMoved || state == Qt::TouchPointPressed) { + if (state == QEventPoint::Updated || state == QEventPoint::Pressed) { // We need a global (screen) position. QWaylandWindow *win = mTouch->mFocus; @@ -1372,23 +1512,25 @@ void QWaylandInputDevice::handleTouchPoint(int id, Qt::TouchPointState state, co return; tp.area = QRectF(0, 0, 8, 8); - QMargins margins = win->frameMargins(); - QPointF localPosition = surfacePosition - QPointF(margins.left(), margins.top()); + QPointF localPosition = win->mapFromWlSurface(surfacePosition); // TODO: This doesn't account for high dpi scaling for the delta, but at least it matches // what we have for mouse input. QPointF delta = localPosition - localPosition.toPoint(); - QPointF globalPosition = win->window()->mapToGlobal(localPosition.toPoint()) + delta; + QPointF globalPosition = win->mapToGlobal(localPosition.toPoint()) + delta; tp.area.moveCenter(globalPosition); } - tp.state = state; - tp.pressure = tp.state == Qt::TouchPointReleased ? 0 : 1; + // If the touch point was pressed earlier this frame, we don't want to overwrite its state. + if (tp.state != QEventPoint::Pressed) + tp.state = QEventPoint::State(state); + + tp.pressure = tp.state == QEventPoint::Released ? 0 : 1; } bool QWaylandInputDevice::Touch::allTouchPointsReleased() { - for (const auto &tp : qAsConst(mPendingTouchPoints)) { - if (tp.state != Qt::TouchPointReleased) + for (const auto &tp : std::as_const(mPendingTouchPoints)) { + if (tp.state != QEventPoint::Released) return false; } return true; @@ -1400,7 +1542,7 @@ void QWaylandInputDevice::Touch::releasePoints() return; for (QWindowSystemInterface::TouchPoint &tp : mPendingTouchPoints) - tp.state = Qt::TouchPointReleased; + tp.state = QEventPoint::Released; touch_frame(); } @@ -1412,26 +1554,31 @@ void QWaylandInputDevice::Touch::touch_frame() QWindow *window = mFocus ? mFocus->window() : nullptr; if (mFocus) { - const QWindowSystemInterface::TouchPoint &tp = mPendingTouchPoints.last(); + // Returns a reference to the last item in the list. The list must not be empty. + // If the list can be empty, call isEmpty() before calling this function. + // See: https://doc.qt.io/qt-5.15/qlist.html#last + if (mPendingTouchPoints.empty()) + return; + const QWindowSystemInterface::TouchPoint &tp = mPendingTouchPoints.constLast(); // When the touch event is received, the global pos is calculated with the margins // in mind. Now we need to adjust again to get the correct local pos back. - QMargins margins = window->frameMargins(); + QMargins margins = mFocus->clientSideMargins(); QPoint p = tp.area.center().toPoint(); - QPointF localPos(window->mapFromGlobal(QPoint(p.x() + margins.left(), p.y() + margins.top()))); + QPointF localPos(mFocus->mapFromGlobal(p) + QPoint(margins.left(), margins.top())); if (mFocus->touchDragDecoration(mParent, localPos, tp.area.center(), tp.state, mParent->modifiers())) return; } - QWindowSystemInterface::handleTouchEvent(window, mParent->mTouchDevice, mPendingTouchPoints); + QWindowSystemInterface::handleTouchEvent(window, mParent->mTime, mParent->mTouchDevice, mPendingTouchPoints, mParent->modifiers()); // Prepare state for next frame const auto prevTouchPoints = mPendingTouchPoints; mPendingTouchPoints.clear(); for (const auto &prevPoint: prevTouchPoints) { // All non-released touch points should be part of the next touch event - if (prevPoint.state != Qt::TouchPointReleased) { + if (prevPoint.state != QEventPoint::Released) { QWindowSystemInterface::TouchPoint tp = prevPoint; - tp.state = Qt::TouchPointStationary; // ... as stationary (unless proven otherwise) + tp.state = QEventPoint::Stationary; // ... as stationary (unless proven otherwise) mPendingTouchPoints.append(tp); } } @@ -1441,3 +1588,5 @@ void QWaylandInputDevice::Touch::touch_frame() } QT_END_NAMESPACE + +#include "moc_qwaylandinputdevice_p.cpp" diff --git a/src/client/qwaylandinputdevice_p.h b/src/client/qwaylandinputdevice_p.h index 60d6f2c17..becd5f9be 100644 --- a/src/client/qwaylandinputdevice_p.h +++ b/src/client/qwaylandinputdevice_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDINPUTDEVICE_H #define QWAYLANDINPUTDEVICE_H @@ -63,9 +27,10 @@ #include <qpa/qwindowsysteminterface.h> #include <QtWaylandClient/private/qwayland-wayland.h> +#include <QtWaylandClient/private/qwayland-pointer-gestures-unstable-v1.h> #if QT_CONFIG(xkbcommon) -#include <QtXkbCommonSupport/private/qxkbcommon_p.h> +#include <QtGui/private/qxkbcommon_p.h> #endif #include <QtCore/QDebug> @@ -89,13 +54,23 @@ class QWaylandDisplay; #if QT_CONFIG(wayland_client_primary_selection) class QWaylandPrimarySelectionDeviceV1; #endif -class QWaylandTextInput; +#if QT_CONFIG(tabletevent) +class QWaylandTabletSeatV2; +#endif +class QWaylandPointerGestures; +class QWaylandPointerGestureSwipe; +class QWaylandPointerGesturePinch; +class QWaylandTextInputInterface; +class QWaylandTextInputMethod; #if QT_CONFIG(cursor) class QWaylandCursorTheme; +class QWaylandCursorShape; class CursorSurface; #endif -class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDevice +Q_DECLARE_LOGGING_CATEGORY(lcQpaWaylandInput); + +class Q_WAYLANDCLIENT_EXPORT QWaylandInputDevice : public QObject , public QtWayland::wl_seat { @@ -109,7 +84,9 @@ public: ~QWaylandInputDevice() override; uint32_t capabilities() const { return mCaps; } + QString seatname() const { return mSeatName; } + QWaylandDisplay *display() const { return mQDisplay; } struct ::wl_seat *wl_seat() { return QtWayland::wl_seat::object(); } #if QT_CONFIG(cursor) @@ -127,8 +104,16 @@ public: QWaylandPrimarySelectionDeviceV1 *primarySelectionDevice() const; #endif - void setTextInput(QWaylandTextInput *textInput); - QWaylandTextInput *textInput() const; +#if QT_CONFIG(tabletevent) + void setTabletSeat(QWaylandTabletSeatV2 *tabletSeat); + QWaylandTabletSeatV2* tabletSeat() const; +#endif + + void setTextInput(QWaylandTextInputInterface *textInput); + QWaylandTextInputInterface *textInput() const; + + void setTextInputMethod(QWaylandTextInputMethod *textInputMethod); + QWaylandTextInputMethod *textInputMethod() const; void removeMouseButtonFromState(Qt::MouseButton button); @@ -150,14 +135,16 @@ public: Keyboard *keyboard() const; Pointer *pointer() const; + QWaylandPointerGestureSwipe *pointerGestureSwipe() const; + QWaylandPointerGesturePinch *pointerGesturePinch() const; Touch *touch() const; -private: +protected: QWaylandDisplay *mQDisplay = nullptr; struct wl_display *mDisplay = nullptr; - int mVersion; uint32_t mCaps = 0; + QString mSeatName; #if QT_CONFIG(cursor) struct CursorState { @@ -178,22 +165,33 @@ private: QScopedPointer<QWaylandPrimarySelectionDeviceV1> mPrimarySelectionDevice; #endif - Keyboard *mKeyboard = nullptr; - Pointer *mPointer = nullptr; - Touch *mTouch = nullptr; + QScopedPointer<Keyboard> mKeyboard; + QScopedPointer<Pointer> mPointer; + QScopedPointer<QWaylandPointerGestureSwipe> mPointerGestureSwipe; + QScopedPointer<QWaylandPointerGesturePinch> mPointerGesturePinch; + QScopedPointer<Touch> mTouch; - QScopedPointer<QWaylandTextInput> mTextInput; + QScopedPointer<QWaylandTextInputInterface> mTextInput; + QScopedPointer<QWaylandTextInputMethod> mTextInputMethod; +#if QT_CONFIG(tabletevent) + QScopedPointer<QWaylandTabletSeatV2> mTabletSeat; +#endif uint32_t mTime = 0; uint32_t mSerial = 0; void seat_capabilities(uint32_t caps) override; - void handleTouchPoint(int id, Qt::TouchPointState state, const QPointF &surfacePosition = QPoint()); + void seat_name(const QString &name) override; + void handleTouchPoint(int id, QEventPoint::State state, const QPointF &surfacePosition = QPoint()); - QTouchDevice *mTouchDevice = nullptr; + QPointingDevice *mTouchDevice = nullptr; + QPointingDevice *mTouchPadDevice = nullptr; friend class QWaylandTouchExtension; friend class QWaylandQtKeyExtension; + friend class QWaylandPointerGestureSwipe; + friend class QWaylandPointerGesturePinch; + friend class QWaylandWindow; }; inline uint32_t QWaylandInputDevice::serial() const @@ -202,7 +200,7 @@ inline uint32_t QWaylandInputDevice::serial() const } -class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDevice::Keyboard : public QObject, public QtWayland::wl_keyboard +class Q_WAYLANDCLIENT_EXPORT QWaylandInputDevice::Keyboard : public QObject, public QtWayland::wl_keyboard { Q_OBJECT @@ -230,18 +228,16 @@ public: void keyboard_repeat_info(int32_t rate, int32_t delay) override; QWaylandInputDevice *mParent = nullptr; - ::wl_surface *mFocus = nullptr; + QPointer<QWaylandSurface> mFocus; uint32_t mNativeModifiers = 0; struct repeatKey { - int key; - uint32_t code; - uint32_t time; + int key = 0; + uint32_t code = 0; + uint32_t time = 0 ; QString text; - Qt::KeyboardModifiers modifiers; - uint32_t nativeVirtualKey; - uint32_t nativeModifiers; + uint32_t nativeVirtualKey = 0; } mRepeatKey; QTimer mRepeatTimer; @@ -254,7 +250,13 @@ public: struct ::wl_keyboard *wl_keyboard() { return QtWayland::wl_keyboard::object(); } -private slots: +#if QT_CONFIG(xkbcommon) + virtual int keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers, xkb_state *state, xkb_keycode_t code) { + return QXkbCommon::keysymToQtKey(keysym, modifiers, state, code); + } +#endif + +private Q_SLOTS: void handleFocusDestroyed(); void handleFocusLost(); @@ -273,7 +275,7 @@ private: friend class QWaylandInputDevice; }; -class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDevice::Pointer : public QObject, public QtWayland::wl_pointer +class Q_WAYLANDCLIENT_EXPORT QWaylandInputDevice::Pointer : public QObject, public QtWayland::wl_pointer { Q_OBJECT public: @@ -281,11 +283,11 @@ public: ~Pointer() override; QWaylandWindow *focusWindow() const; #if QT_CONFIG(cursor) - QString cursorThemeName() const; - int cursorSize() const; // in surface coordinates int idealCursorScale() const; void updateCursorTheme(); void updateCursor(); + void cursorTimerCallback(); + void cursorFrameCallback(); CursorSurface *getOrCreateCursorSurface(); #endif QWaylandInputDevice *seat() const { return mParent; } @@ -307,8 +309,10 @@ protected: void pointer_axis_stop(uint32_t time, uint32_t axis) override; void pointer_axis_discrete(uint32_t axis, int32_t value) override; void pointer_frame() override; + void pointer_axis_value120(uint32_t axis, int32_t value120) override; + void pointer_axis_relative_direction(uint32_t axis, uint32_t direction) override; -private slots: +private Q_SLOTS: void handleFocusDestroyed() { invalidateFocus(); } private: @@ -322,25 +326,28 @@ public: uint32_t mEnterSerial = 0; #if QT_CONFIG(cursor) struct { + QScopedPointer<QWaylandCursorShape> shape; QWaylandCursorTheme *theme = nullptr; int themeBufferScale = 0; QScopedPointer<CursorSurface> surface; + QTimer frameTimer; + bool gotFrameCallback = false; + bool gotTimerCallback = false; } mCursor; #endif QPointF mSurfacePos; QPointF mGlobalPos; Qt::MouseButtons mButtons = Qt::NoButton; -#if QT_CONFIG(cursor) - wl_buffer *mCursorBuffer = nullptr; - Qt::CursorShape mCursorShape = Qt::BitmapCursor; -#endif + Qt::MouseButton mLastButton = Qt::NoButton; struct FrameData { QWaylandPointerEvent *event = nullptr; QPointF delta; - QPoint discreteDelta; + QPoint delta120; axis_source axisSource = axis_source_wheel; + bool verticalAxisInverted = false; + bool horizontalAxisInverted = false; void resetScrollData(); bool hasPixelDelta() const; @@ -360,7 +367,7 @@ private: //TODO: should other methods be private as well? bool isDefinitelyTerminated(axis_source source) const; }; -class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDevice::Touch : public QtWayland::wl_touch +class Q_WAYLANDCLIENT_EXPORT QWaylandInputDevice::Touch : public QtWayland::wl_touch { public: Touch(QWaylandInputDevice *p); @@ -396,33 +403,25 @@ class QWaylandPointerEvent { Q_GADGET public: - enum Type { - Enter, - Leave, - Motion, - Press, - Release, - Wheel - }; - Q_ENUM(Type) - - inline QWaylandPointerEvent(Type type, Qt::ScrollPhase phase, QWaylandWindow *surface, + inline QWaylandPointerEvent(QEvent::Type type, Qt::ScrollPhase phase, QWaylandWindow *surface, ulong timestamp, const QPointF &localPos, const QPointF &globalPos, - Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers) + Qt::MouseButtons buttons, Qt::MouseButton button, + Qt::KeyboardModifiers modifiers) : type(type) , phase(phase) , timestamp(timestamp) , local(localPos) , global(globalPos) , buttons(buttons) + , button(button) , modifiers(modifiers) , surface(surface) {} - inline QWaylandPointerEvent(Type type, Qt::ScrollPhase phase, QWaylandWindow *surface, + inline QWaylandPointerEvent(QEvent::Type type, Qt::ScrollPhase phase, QWaylandWindow *surface, ulong timestamp, const QPointF &local, const QPointF &global, const QPoint &pixelDelta, const QPoint &angleDelta, Qt::MouseEventSource source, - Qt::KeyboardModifiers modifiers) + Qt::KeyboardModifiers modifiers, bool inverted) : type(type) , phase(phase) , timestamp(timestamp) @@ -433,20 +432,80 @@ public: , angleDelta(angleDelta) , source(source) , surface(surface) + , inverted(inverted) {} - Type type; + QEvent::Type type = QEvent::None; Qt::ScrollPhase phase = Qt::NoScrollPhase; ulong timestamp = 0; QPointF local; QPointF global; Qt::MouseButtons buttons; + Qt::MouseButton button = Qt::NoButton; // Button that caused the event (QMouseEvent::button) Qt::KeyboardModifiers modifiers; QPoint pixelDelta; QPoint angleDelta; Qt::MouseEventSource source = Qt::MouseEventNotSynthesized; QPointer<QWaylandWindow> surface; + bool inverted = false; +}; + +#ifndef QT_NO_GESTURES +class QWaylandPointerGestureSwipeEvent +{ + Q_GADGET +public: + inline QWaylandPointerGestureSwipeEvent(QWaylandWindow *surface, Qt::GestureState state, + ulong timestamp, const QPointF &local, + const QPointF &global, uint fingers, const QPointF& delta) + : surface(surface) + , state(state) + , timestamp(timestamp) + , local(local) + , global(global) + , fingers(fingers) + , delta(delta) + {} + + QPointer<QWaylandWindow> surface; + Qt::GestureState state = Qt::GestureState::NoGesture; + ulong timestamp = 0; + QPointF local; + QPointF global; + uint fingers = 0; + QPointF delta; +}; + +class QWaylandPointerGesturePinchEvent +{ + Q_GADGET +public: + inline QWaylandPointerGesturePinchEvent(QWaylandWindow *surface, Qt::GestureState state, + ulong timestamp, const QPointF &local, + const QPointF &global, uint fingers, const QPointF& delta, + qreal scale_delta, qreal rotation_delta) + : surface(surface) + , state(state) + , timestamp(timestamp) + , local(local) + , global(global) + , fingers(fingers) + , delta(delta) + , scale_delta(scale_delta) + , rotation_delta(rotation_delta) + {} + + QPointer<QWaylandWindow> surface; + Qt::GestureState state = Qt::GestureState::NoGesture; + ulong timestamp = 0; + QPointF local; + QPointF global; + uint fingers = 0; + QPointF delta; + qreal scale_delta = 0; + qreal rotation_delta = 0; }; +#endif // #ifndef QT_NO_GESTURES } diff --git a/src/client/qwaylandinputmethodcontext.cpp b/src/client/qwaylandinputmethodcontext.cpp new file mode 100644 index 000000000..2733e4f3a --- /dev/null +++ b/src/client/qwaylandinputmethodcontext.cpp @@ -0,0 +1,401 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwaylandinputmethodcontext_p.h" +#include "qwaylanddisplay_p.h" +#include "qwaylandinputdevice_p.h" + +#include <QtGui/qguiapplication.h> +#include <QtGui/qtextformat.h> +#include <QtGui/private/qguiapplication_p.h> + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(qLcQpaInputMethods) + +namespace QtWaylandClient { + +static constexpr int maxStringSize = 1000; // actual max is 4096/3 + +QWaylandTextInputMethod::QWaylandTextInputMethod(QWaylandDisplay *display, struct ::qt_text_input_method_v1 *textInputMethod) + : QtWayland::qt_text_input_method_v1(textInputMethod) +{ + Q_UNUSED(display); +} + +QWaylandTextInputMethod::~QWaylandTextInputMethod() +{ + qt_text_input_method_v1_destroy(object()); +} + +void QWaylandTextInputMethod::text_input_method_v1_visible_changed(int32_t visible) +{ + if (m_isVisible != visible) { + m_isVisible = visible; + QGuiApplicationPrivate::platformIntegration()->inputContext()->emitInputPanelVisibleChanged(); + } +} + +void QWaylandTextInputMethod::text_input_method_v1_locale_changed(const QString &localeName) +{ + m_locale = QLocale(localeName); +} + +void QWaylandTextInputMethod::text_input_method_v1_input_direction_changed(int32_t inputDirection) +{ + m_layoutDirection = Qt::LayoutDirection(inputDirection); +} + +void QWaylandTextInputMethod::text_input_method_v1_keyboard_rectangle_changed(wl_fixed_t x, wl_fixed_t y, wl_fixed_t width, wl_fixed_t height) +{ + const QRectF keyboardRectangle(wl_fixed_to_double(x), + wl_fixed_to_double(y), + wl_fixed_to_double(width), + wl_fixed_to_double(height)); + if (m_keyboardRect != keyboardRectangle) { + m_keyboardRect = keyboardRectangle; + QGuiApplicationPrivate::platformIntegration()->inputContext()->emitKeyboardRectChanged(); + } +} + +void QWaylandTextInputMethod::text_input_method_v1_start_input_method_event(uint32_t serial, int32_t surrounding_text_offset) +{ + if (m_pendingInputMethodEvents.contains(serial)) { + qCWarning(qLcQpaInputMethods) << "Input method event with serial" << serial << "already started"; + return; + } + + m_pendingInputMethodEvents[serial] = QList<QInputMethodEvent::Attribute>{}; + m_offsetFromCompositor[serial] = surrounding_text_offset; +} + +// We need to keep surrounding text below maxStringSize characters, with cursorPos centered in that substring + +static int calculateOffset(const QString &text, int cursorPos) +{ + int size = text.size(); + int halfSize = maxStringSize/2; + if (size <= maxStringSize || cursorPos < halfSize) + return 0; + if (cursorPos > size - halfSize) + return size - maxStringSize; + return cursorPos - halfSize; +} + +static QString mapSurroundingTextToCompositor(const QString &s, int offset) +{ + return s.mid(offset, maxStringSize); +} + +static int mapPositionToCompositor(int pos, int offset) +{ + return pos - offset; +} + +static int mapPositionFromCompositor(int pos, int offset) +{ + return pos + offset; +} + +void QWaylandTextInputMethod::text_input_method_v1_input_method_event_attribute(uint32_t serial, int32_t type, int32_t start, int32_t length, const QString &value) +{ + if (!m_pendingInputMethodEvents.contains(serial)) { + qCWarning(qLcQpaInputMethods) << "Input method event with serial" << serial << "does not exist"; + return; + } + + int startMapped = mapPositionFromCompositor(start, m_offsetFromCompositor[serial]); + QList<QInputMethodEvent::Attribute> &attributes = m_pendingInputMethodEvents[serial]; + switch (type) { + case QInputMethodEvent::Selection: + attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::AttributeType(type), startMapped, length)); + break; + case QInputMethodEvent::Cursor: + attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::AttributeType(type), start, length, QColor::fromString(value))); + break; + case QInputMethodEvent::TextFormat: + { + QTextCharFormat textFormat; + textFormat.setProperty(QTextFormat::FontUnderline, true); + textFormat.setProperty(QTextFormat::TextUnderlineStyle, QTextCharFormat::SingleUnderline); + attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::AttributeType(type), start, length, textFormat)); + break; + } + case QInputMethodEvent::Language: + case QInputMethodEvent::Ruby: + attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::AttributeType(type), start, length, value)); + break; + }; +} + +void QWaylandTextInputMethod::sendInputState(QInputMethodQueryEvent *event, Qt::InputMethodQueries queries) +{ + int cursorPosition = event->value(Qt::ImCursorPosition).toInt(); + int anchorPosition = event->value(Qt::ImAnchorPosition).toInt(); + QString surroundingText = event->value(Qt::ImSurroundingText).toString(); + int offset = calculateOffset(surroundingText, cursorPosition); + + if (queries & Qt::ImCursorPosition) + update_cursor_position(mapPositionToCompositor(cursorPosition, offset)); + if (queries & Qt::ImSurroundingText) + update_surrounding_text(mapSurroundingTextToCompositor(surroundingText, offset), offset); + if (queries & Qt::ImAnchorPosition) + update_anchor_position(mapPositionToCompositor(anchorPosition, offset)); + if (queries & Qt::ImAbsolutePosition) + update_absolute_position(event->value(Qt::ImAbsolutePosition).toInt()); // do not map: this is the position in the whole document +} + + +void QWaylandTextInputMethod::text_input_method_v1_end_input_method_event(uint32_t serial, const QString &commitString, const QString &preeditString, int32_t replacementStart, int32_t replacementLength) +{ + if (!m_pendingInputMethodEvents.contains(serial)) { + qCWarning(qLcQpaInputMethods) << "Input method event with serial" << serial << "does not exist"; + return; + } + + QList<QInputMethodEvent::Attribute> attributes = m_pendingInputMethodEvents.take(serial); + m_offsetFromCompositor.remove(serial); + if (QGuiApplication::focusObject() != nullptr) { + QInputMethodEvent event(preeditString, attributes); + event.setCommitString(commitString, replacementStart, replacementLength); + QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event); + } + + // Send current state to make sure it matches + if (QGuiApplication::focusObject() != nullptr) { + QInputMethodQueryEvent event(Qt::ImCursorPosition | Qt::ImSurroundingText | Qt::ImAnchorPosition | Qt::ImAbsolutePosition); + QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event); + sendInputState(&event); + } + + acknowledge_input_method(); +} + +void QWaylandTextInputMethod::text_input_method_v1_key(int32_t type, + int32_t key, + int32_t modifiers, + int32_t autoRepeat, + int32_t count, + int32_t nativeScanCode, + int32_t nativeVirtualKey, + int32_t nativeModifiers, + const QString &text) +{ + if (QGuiApplication::focusObject() != nullptr) { + QKeyEvent event(QKeyEvent::Type(type), + key, + Qt::KeyboardModifiers(modifiers), + nativeScanCode, + nativeVirtualKey, + nativeModifiers, + text, + autoRepeat, + count); + QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event); + } +} + +void QWaylandTextInputMethod::text_input_method_v1_enter(struct ::wl_surface *surface) +{ + m_surface = surface; +} + +void QWaylandTextInputMethod::text_input_method_v1_leave(struct ::wl_surface *surface) +{ + if (surface != m_surface) { + qCWarning(qLcQpaInputMethods) << "Got leave event for surface without corresponding enter"; + } else { + m_surface = nullptr; + } +} + +QWaylandInputMethodContext::QWaylandInputMethodContext(QWaylandDisplay *display) + : m_display(display) +{ +} + +QWaylandInputMethodContext::~QWaylandInputMethodContext() +{ +} + +bool QWaylandInputMethodContext::isValid() const +{ + return m_display->textInputMethodManager() != nullptr; +} + +void QWaylandInputMethodContext::reset() +{ + QWaylandTextInputMethod *inputMethod = textInputMethod(); + if (inputMethod != nullptr) + inputMethod->reset(); +} + +void QWaylandInputMethodContext::commit() +{ + QWaylandTextInputMethod *inputMethod = textInputMethod(); + if (inputMethod != nullptr) + inputMethod->commit(); + + m_display->forceRoundTrip(); +} + +void QWaylandInputMethodContext::update(Qt::InputMethodQueries queries) +{ + wl_surface *currentSurface = m_currentWindow != nullptr && m_currentWindow->handle() != nullptr + ? static_cast<QWaylandWindow *>(m_currentWindow->handle())->wlSurface() + : nullptr; + if (currentSurface != nullptr && !inputMethodAccepted()) { + textInputMethod()->disable(currentSurface); + m_currentWindow.clear(); + } else if (currentSurface == nullptr && inputMethodAccepted()) { + QWindow *window = QGuiApplication::focusWindow(); + currentSurface = window != nullptr && window->handle() != nullptr + ? static_cast<QWaylandWindow *>(window->handle())->wlSurface() + : nullptr; + if (currentSurface != nullptr) { + textInputMethod()->disable(currentSurface); + m_currentWindow = window; + } + } + + queries &= (Qt::ImEnabled + | Qt::ImHints + | Qt::ImCursorRectangle + | Qt::ImCursorPosition + | Qt::ImSurroundingText + | Qt::ImCurrentSelection + | Qt::ImAnchorPosition + | Qt::ImTextAfterCursor + | Qt::ImTextBeforeCursor + | Qt::ImPreferredLanguage); + + const Qt::InputMethodQueries queriesNeedingOffset = Qt::ImCursorPosition | Qt::ImSurroundingText | Qt::ImAnchorPosition; + if (queries & queriesNeedingOffset) + queries |= queriesNeedingOffset; + + QWaylandTextInputMethod *inputMethod = textInputMethod(); + if (inputMethod != nullptr && QGuiApplication::focusObject() != nullptr) { + QInputMethodQueryEvent event(queries); + QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event); + + inputMethod->start_update(int(queries)); + + if (queries & Qt::ImHints) + inputMethod->update_hints(event.value(Qt::ImHints).toInt()); + + if (queries & Qt::ImCursorRectangle) { + QRect rect = event.value(Qt::ImCursorRectangle).toRect(); + inputMethod->update_cursor_rectangle(rect.x(), rect.y(), rect.width(), rect.height()); + } + + inputMethod->sendInputState(&event, queries); + + if (queries & Qt::ImPreferredLanguage) + inputMethod->update_preferred_language(event.value(Qt::ImPreferredLanguage).toString()); + + inputMethod->end_update(); + + // ### Should we do a display sync here and ignore all events until it is received? + } +} + +void QWaylandInputMethodContext::invokeAction(QInputMethod::Action action, int cursorPosition) +{ + QWaylandTextInputMethod *inputMethod = textInputMethod(); + if (inputMethod != nullptr) + inputMethod->invoke_action(int(action), cursorPosition); +} + +void QWaylandInputMethodContext::showInputPanel() +{ + QWaylandTextInputMethod *inputMethod = textInputMethod(); + if (inputMethod != nullptr) + inputMethod->show_input_panel(); +} + +void QWaylandInputMethodContext::hideInputPanel() +{ + QWaylandTextInputMethod *inputMethod = textInputMethod(); + if (inputMethod != nullptr) + inputMethod->hide_input_panel(); +} + +bool QWaylandInputMethodContext::isInputPanelVisible() const +{ + QWaylandTextInputMethod *inputMethod = textInputMethod(); + if (inputMethod != nullptr) + return inputMethod->isVisible(); + else + return false; +} + +QRectF QWaylandInputMethodContext::keyboardRect() const +{ + QWaylandTextInputMethod *inputMethod = textInputMethod(); + if (inputMethod != nullptr) + return inputMethod->keyboardRect(); + else + return QRectF(); +} + +QLocale QWaylandInputMethodContext::locale() const +{ + QWaylandTextInputMethod *inputMethod = textInputMethod(); + if (inputMethod != nullptr) + return inputMethod->locale(); + else + return QLocale(); +} + +Qt::LayoutDirection QWaylandInputMethodContext::inputDirection() const +{ + QWaylandTextInputMethod *inputMethod = textInputMethod(); + if (inputMethod != nullptr) + return inputMethod->inputDirection(); + else + return Qt::LeftToRight; +} + +void QWaylandInputMethodContext::setFocusObject(QObject *) +{ + QWaylandTextInputMethod *inputMethod = textInputMethod(); + if (inputMethod == nullptr) + return; + + if (inputMethod->isVisible() && !inputMethodAccepted()) + inputMethod->hide_input_panel(); + + QWindow *window = QGuiApplication::focusWindow(); + + if (m_currentWindow != nullptr && m_currentWindow->handle() != nullptr) { + if (m_currentWindow.data() != window || !inputMethodAccepted()) { + auto *surface = static_cast<QWaylandWindow *>(m_currentWindow->handle())->wlSurface(); + if (surface) + inputMethod->disable(surface); + m_currentWindow.clear(); + } + } + + if (window != nullptr && window->handle() != nullptr && inputMethodAccepted()) { + if (m_currentWindow.data() != window) { + auto *surface = static_cast<QWaylandWindow *>(window->handle())->wlSurface(); + if (surface != nullptr) { + inputMethod->enable(surface); + m_currentWindow = window; + } + } + + update(Qt::ImQueryAll); + } +} + +QWaylandTextInputMethod *QWaylandInputMethodContext::textInputMethod() const +{ + return m_display->defaultInputDevice() ? m_display->defaultInputDevice()->textInputMethod() : nullptr; +} + +} // QtWaylandClient + +QT_END_NAMESPACE + +#include "moc_qwaylandinputmethodcontext_p.cpp" diff --git a/src/client/qwaylandinputmethodcontext_p.h b/src/client/qwaylandinputmethodcontext_p.h new file mode 100644 index 000000000..85ef65601 --- /dev/null +++ b/src/client/qwaylandinputmethodcontext_p.h @@ -0,0 +1,118 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWAYLANDINPUTMETHODCONTEXT_P_H +#define QWAYLANDINPUTMETHODCONTEXT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/qpa/qplatforminputcontext.h> +#include <QtGui/qevent.h> +#include <QtCore/qlocale.h> +#include <QtCore/qpointer.h> +#include <QtCore/qlist.h> +#include <QtCore/qhash.h> + +#include <QtWaylandClient/private/qwayland-qt-text-input-method-unstable-v1.h> +#include <QtCore/private/qglobal_p.h> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + class QWaylandDisplay; + +class QWaylandTextInputMethod : public QtWayland::qt_text_input_method_v1 +{ +public: + QWaylandTextInputMethod(QWaylandDisplay *display, struct ::qt_text_input_method_v1 *textInputMethod); + ~QWaylandTextInputMethod() override; + + void text_input_method_v1_visible_changed(int32_t visible) override; + void text_input_method_v1_enter(struct ::wl_surface *surface) override; + void text_input_method_v1_leave(struct ::wl_surface *surface) override; + void text_input_method_v1_locale_changed(const QString &localeName) override; + void text_input_method_v1_input_direction_changed(int32_t inputDirection) override; + void text_input_method_v1_keyboard_rectangle_changed(wl_fixed_t x, wl_fixed_t y, wl_fixed_t width, wl_fixed_t height) override; + void text_input_method_v1_key(int32_t type, int32_t key, int32_t modifiers, int32_t autoRepeat, int32_t count, int32_t nativeScanCode, int32_t nativeVirtualKey, int32_t nativeModifiers, const QString &text) override; + void text_input_method_v1_start_input_method_event(uint32_t serial, int32_t surrounding_text_offset) override; + void text_input_method_v1_end_input_method_event(uint32_t serial, const QString &commitString, const QString &preeditString, int32_t replacementStart, int32_t replacementLength) override; + void text_input_method_v1_input_method_event_attribute(uint32_t serial, int32_t type, int32_t start, int32_t length, const QString &value) override; + + inline bool isVisible() const + { + return m_isVisible; + } + + inline QRectF keyboardRect() const + { + return m_keyboardRect; + } + + inline QLocale locale() const + { + return m_locale; + } + + inline Qt::LayoutDirection inputDirection() const + { + return m_layoutDirection; + } + + void sendInputState(QInputMethodQueryEvent *state, Qt::InputMethodQueries queries = Qt::ImQueryInput); + +private: + QHash<int, QList<QInputMethodEvent::Attribute> > m_pendingInputMethodEvents; + QHash<int,int> m_offsetFromCompositor; + + struct ::wl_surface *m_surface; + + // Cached state + bool m_isVisible = false; + QRectF m_keyboardRect; + QLocale m_locale; + Qt::LayoutDirection m_layoutDirection; +}; + +class QWaylandInputMethodContext : public QPlatformInputContext +{ + Q_OBJECT +public: + QWaylandInputMethodContext(QWaylandDisplay *display); + ~QWaylandInputMethodContext() override; + + bool isValid() const override; + void reset() override; + void commit() override; + void update(Qt::InputMethodQueries) override; + void invokeAction(QInputMethod::Action, int cursorPosition) override; + void showInputPanel() override; + void hideInputPanel() override; + + bool isInputPanelVisible() const override; + QRectF keyboardRect() const override; + QLocale locale() const override; + Qt::LayoutDirection inputDirection() const override; + + void setFocusObject(QObject *object) override; + +private: + QWaylandTextInputMethod *textInputMethod() const; + + QWaylandDisplay *m_display; + QPointer<QWindow> m_currentWindow; +}; + +} // QtWaylandClient + +QT_END_NAMESPACE + +#endif // QWAYLANDINPUTMETHODCONTEXT_P_H diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp index 9bdd9cc12..c5eb1e96a 100644 --- a/src/client/qwaylandintegration.cpp +++ b/src/client/qwaylandintegration.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the config.tests of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandintegration_p.h" @@ -43,6 +7,7 @@ #include "qwaylandshmwindow_p.h" #include "qwaylandinputdevice_p.h" #include "qwaylandinputcontext_p.h" +#include "qwaylandinputmethodcontext_p.h" #include "qwaylandshmbackingstore_p.h" #include "qwaylandnativeinterface_p.h" #if QT_CONFIG(clipboard) @@ -50,23 +15,27 @@ #endif #include "qwaylanddnd_p.h" #include "qwaylandwindowmanagerintegration_p.h" +#include "qwaylandplatformservices_p.h" #include "qwaylandscreen_p.h" +#include "qwaylandcursor_p.h" #if defined(Q_OS_MACOS) -# include <QtFontDatabaseSupport/private/qcoretextfontdatabase_p.h> -# include <QtFontDatabaseSupport/private/qfontengine_coretext_p.h> +# include <QtGui/private/qcoretextfontdatabase_p.h> +# include <QtGui/private/qfontengine_coretext_p.h> #else -# include <QtFontDatabaseSupport/private/qgenericunixfontdatabase_p.h> +# include <QtGui/private/qgenericunixfontdatabase_p.h> #endif -#include <QtEventDispatcherSupport/private/qgenericunixeventdispatcher_p.h> -#include <QtThemeSupport/private/qgenericunixthemes_p.h> +#include <QtGui/private/qgenericunixeventdispatcher_p.h> +#include <QtGui/private/qgenericunixthemes_p.h> #include <QtGui/private/qguiapplication_p.h> #include <qpa/qwindowsysteminterface.h> #include <qpa/qplatformcursor.h> #include <QtGui/QSurfaceFormat> +#if QT_CONFIG(opengl) #include <QtGui/QOpenGLContext> +#endif // QT_CONFIG(opengl) #include <QSocketNotifier> #include <qpa/qplatforminputcontextfactory_p.h> @@ -79,19 +48,21 @@ #include "qwaylandserverbufferintegration_p.h" #include "qwaylandserverbufferintegrationfactory_p.h" +#include "qwaylandshellsurface_p.h" #include "qwaylandshellintegration_p.h" #include "qwaylandshellintegrationfactory_p.h" #include "qwaylandinputdeviceintegration_p.h" #include "qwaylandinputdeviceintegrationfactory_p.h" +#include "qwaylandwindow_p.h" #if QT_CONFIG(accessibility_atspi_bridge) -#include <QtLinuxAccessibilitySupport/private/bridge_p.h> +#include <QtGui/private/qspiaccessiblebridge_p.h> #endif #if QT_CONFIG(xkbcommon) -#include <QtXkbCommonSupport/private/qxkbcommon_p.h> +#include <QtGui/private/qxkbcommon_p.h> #endif #if QT_CONFIG(vulkan) @@ -103,32 +74,32 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { +QWaylandIntegration *QWaylandIntegration::sInstance = nullptr; + QWaylandIntegration::QWaylandIntegration() #if defined(Q_OS_MACOS) : mFontDb(new QCoreTextFontDatabaseEngineFactory<QCoreTextFontEngine>) #else : mFontDb(new QGenericUnixFontDatabase()) #endif - , mNativeInterface(new QWaylandNativeInterface(this)) { - initializeInputDeviceIntegration(); mDisplay.reset(new QWaylandDisplay(this)); - if (!mDisplay->isInitialized()) { - mFailed = true; - return; - } -#if QT_CONFIG(clipboard) - mClipboard.reset(new QWaylandClipboard(mDisplay.data())); -#endif -#if QT_CONFIG(draganddrop) - mDrag.reset(new QWaylandDrag(mDisplay.data())); -#endif + mPlatformServices.reset(new QWaylandPlatformServices(mDisplay.data())); - reconfigureInputContext(); + QWaylandWindow::fixedToplevelPositions = + !qEnvironmentVariableIsSet("QT_WAYLAND_DISABLE_FIXED_POSITIONS"); + + sInstance = this; } QWaylandIntegration::~QWaylandIntegration() { + sInstance = nullptr; +} + +bool QWaylandIntegration::init() +{ + return mDisplay->initialize(); } QPlatformNativeInterface * QWaylandIntegration::nativeInterface() const @@ -153,6 +124,8 @@ bool QWaylandIntegration::hasCapability(QPlatformIntegration::Capability cap) co return true; case WindowActivation: return false; + case ScreenWindowGrabbing: // whether QScreen::grabWindow() is supported + return false; default: return QPlatformIntegration::hasCapability(cap); } } @@ -165,10 +138,10 @@ QPlatformWindow *QWaylandIntegration::createPlatformWindow(QWindow *window) cons #if QT_CONFIG(vulkan) if (window->surfaceType() == QSurface::VulkanSurface) - return new QWaylandVulkanWindow(window); + return new QWaylandVulkanWindow(window, mDisplay.data()); #endif // QT_CONFIG(vulkan) - return new QWaylandShmWindow(window); + return new QWaylandShmWindow(window, mDisplay.data()); } #if QT_CONFIG(opengl) @@ -182,7 +155,7 @@ QPlatformOpenGLContext *QWaylandIntegration::createPlatformOpenGLContext(QOpenGL QPlatformBackingStore *QWaylandIntegration::createPlatformBackingStore(QWindow *window) const { - return new QWaylandShmBackingStore(window); + return new QWaylandShmBackingStore(window, mDisplay.data()); } QAbstractEventDispatcher *QWaylandIntegration::createEventDispatcher() const @@ -190,20 +163,39 @@ QAbstractEventDispatcher *QWaylandIntegration::createEventDispatcher() const return createUnixEventDispatcher(); } +QPlatformNativeInterface *QWaylandIntegration::createPlatformNativeInterface() +{ + return new QWaylandNativeInterface(this); +} + +// Support platform specific initialization +void QWaylandIntegration::initializePlatform() +{ + mDisplay->initEventThread(); + + mNativeInterface.reset(createPlatformNativeInterface()); + initializeInputDeviceIntegration(); +#if QT_CONFIG(clipboard) + mClipboard.reset(new QWaylandClipboard(mDisplay.data())); +#endif +#if QT_CONFIG(draganddrop) + mDrag.reset(new QWaylandDrag(mDisplay.data())); +#endif + + reconfigureInputContext(); +} + void QWaylandIntegration::initialize() { + initializePlatform(); + + // Call this after initializing event thread for QWaylandDisplay::flushRequests() QAbstractEventDispatcher *dispatcher = QGuiApplicationPrivate::eventDispatcher; QObject::connect(dispatcher, SIGNAL(aboutToBlock()), mDisplay.data(), SLOT(flushRequests())); QObject::connect(dispatcher, SIGNAL(awake()), mDisplay.data(), SLOT(flushRequests())); - int fd = wl_display_get_fd(mDisplay->wl_display()); - QSocketNotifier *sn = new QSocketNotifier(fd, QSocketNotifier::Read, mDisplay.data()); - QObject::connect(sn, SIGNAL(activated(int)), mDisplay.data(), SLOT(flushRequests())); - - if (mDisplay->screens().isEmpty()) { - qWarning() << "Running on a compositor with no screens is not supported"; - ::exit(EXIT_FAILURE); - } + // Qt does not support running with no screens + mDisplay->ensureScreen(); } QPlatformFontDatabase *QWaylandIntegration::fontDatabase() const @@ -235,13 +227,6 @@ QVariant QWaylandIntegration::styleHint(StyleHint hint) const if (hint == ShowIsFullScreen && mDisplay->windowManagerIntegration()) return mDisplay->windowManagerIntegration()->showIsFullScreen(); - switch (hint) { - case QPlatformIntegration::FontSmoothingGamma: - return qreal(1.0); - default: - break; - } - return QPlatformIntegration::styleHint(hint); } @@ -263,7 +248,7 @@ QPlatformAccessibility *QWaylandIntegration::accessibility() const QPlatformServices *QWaylandIntegration::services() const { - return mDisplay->windowManagerIntegration(); + return mPlatformServices.data(); } QWaylandDisplay *QWaylandIntegration::display() const @@ -271,6 +256,14 @@ QWaylandDisplay *QWaylandIntegration::display() const return mDisplay.data(); } +Qt::KeyboardModifiers QWaylandIntegration::queryKeyboardModifiers() const +{ + if (auto *seat = mDisplay->currentInputDevice(); seat && seat->keyboardFocus()) { + return seat->modifiers(); + } + return Qt::NoModifier; +} + QList<int> QWaylandIntegration::possibleKeys(const QKeyEvent *event) const { if (auto *seat = mDisplay->currentInputDevice()) @@ -288,6 +281,16 @@ QPlatformTheme *QWaylandIntegration::createPlatformTheme(const QString &name) co return QGenericUnixTheme::createUnixTheme(name); } +QWaylandScreen *QWaylandIntegration::createPlatformScreen(QWaylandDisplay *waylandDisplay, int version, uint32_t id) const +{ + return new QWaylandScreen(waylandDisplay, version, id); +} + +QWaylandCursor *QWaylandIntegration::createPlatformCursor(QWaylandDisplay *display) const +{ + return new QWaylandCursor(display); +} + #if QT_CONFIG(vulkan) QPlatformVulkanInstance *QWaylandIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const { @@ -405,17 +408,11 @@ void QWaylandIntegration::initializeShellIntegration() preferredShells = targetKeys.split(QLatin1Char(';')); } else { preferredShells << QLatin1String("xdg-shell"); - preferredShells << QLatin1String("xdg-shell-v6"); - QString useXdgShell = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_USE_XDG_SHELL")); - if (!useXdgShell.isEmpty() && useXdgShell != QLatin1String("0")) { - qWarning() << "QT_WAYLAND_USE_XDG_SHELL is deprecated, " - "please specify the shell using QT_WAYLAND_SHELL_INTEGRATION instead"; - preferredShells << QLatin1String("xdg-shell-v5"); - } preferredShells << QLatin1String("wl-shell") << QLatin1String("ivi-shell"); + preferredShells << QLatin1String("qt-shell"); } - for (const QString &preferredShell : qAsConst(preferredShells)) { + for (const QString &preferredShell : std::as_const(preferredShells)) { mShellIntegration.reset(createShellIntegration(preferredShell)); if (mShellIntegration) { qCDebug(lcQpaWayland, "Using the '%s' shell integration", qPrintable(preferredShell)); @@ -427,9 +424,11 @@ void QWaylandIntegration::initializeShellIntegration() qCWarning(lcQpaWayland) << "Loading shell integration failed."; qCWarning(lcQpaWayland) << "Attempted to load the following shells" << preferredShells; } + + QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false); } -QWaylandInputDevice *QWaylandIntegration::createInputDevice(QWaylandDisplay *display, int version, uint32_t id) +QWaylandInputDevice *QWaylandIntegration::createInputDevice(QWaylandDisplay *display, int version, uint32_t id) const { if (mInputDeviceIntegration) { return mInputDeviceIntegration->createInputDevice(display, version, id); @@ -465,30 +464,44 @@ void QWaylandIntegration::reconfigureInputContext() return; } - const QString &requested = QPlatformInputContextFactory::requested(); - if (requested == QLatin1String("qtvirtualkeyboard")) + auto requested = QPlatformInputContextFactory::requested(); + if (requested.contains(QLatin1String("qtvirtualkeyboard"))) qCWarning(lcQpaWayland) << "qtvirtualkeyboard currently is not supported at client-side," - " use QT_IM_MODULE=qtvirtualkeyboard at compositor-side."; + " use QT_IM_MODULES=qtvirtualkeyboard at compositor-side."; - if (requested.isNull()) - mInputContext.reset(new QWaylandInputContext(mDisplay.data())); - else - mInputContext.reset(QPlatformInputContextFactory::create(requested)); + if (mDisplay->isWaylandInputContextRequested() + && !requested.contains(QLatin1String(WAYLAND_IM_KEY))) + requested.append(QLatin1String(WAYLAND_IM_KEY)); const QString defaultInputContext(QStringLiteral("compose")); - if ((!mInputContext || !mInputContext->isValid()) && requested != defaultInputContext) - mInputContext.reset(QPlatformInputContextFactory::create(defaultInputContext)); + if (!requested.contains(defaultInputContext)) + requested.append(defaultInputContext); + + for (const QString &imKey : requested) { + if (imKey == QLatin1String(WAYLAND_IM_KEY)) { + Q_ASSERT(mDisplay->isWaylandInputContextRequested()); + if (mDisplay->textInputMethodManager() != nullptr) + mInputContext.reset(new QWaylandInputMethodContext(mDisplay.data())); + else if (mDisplay->textInputManagerv1() != nullptr + || mDisplay->textInputManagerv2() != nullptr + || mDisplay->textInputManagerv3() != nullptr) + mInputContext.reset(new QWaylandInputContext(mDisplay.data())); + } else { + mInputContext.reset(QPlatformInputContextFactory::create(imKey)); + } + + if (mInputContext && mInputContext->isValid()) + break; + } #if QT_CONFIG(xkbcommon) QXkbCommon::setXkbContext(mInputContext.data(), mDisplay->xkbContext()); + if (QWaylandInputContext* waylandInput = qobject_cast<QWaylandInputContext*>(mInputContext.get())) { + waylandInput->setXkbContext(mDisplay->xkbContext()); + } #endif - // Even if compositor-side input context handling has been requested, we fallback to - // client-side handling if compositor does not provide the text-input extension. This - // is why we need to check here which input context actually is being used. - mDisplay->mUsingInputContextFromCompositor = qobject_cast<QWaylandInputContext *>(mInputContext.data()); - - qCDebug(lcQpaWayland) << "using input method:" << inputContext()->metaObject()->className(); + qCDebug(lcQpaWayland) << "using input method:" << (inputContext() ? inputContext()->metaObject()->className() : "<none>"); } QWaylandShellIntegration *QWaylandIntegration::createShellIntegration(const QString &integrationName) @@ -501,6 +514,21 @@ QWaylandShellIntegration *QWaylandIntegration::createShellIntegration(const QStr } } +void QWaylandIntegration::reset() +{ + mServerBufferIntegration.reset(); + mServerBufferIntegrationInitialized = false; + + mInputDeviceIntegration.reset(); + + mClientBufferIntegration.reset(); + mClientBufferIntegrationInitialized = false; +} + +void QWaylandIntegration::setApplicationBadge(qint64 number) +{ + mPlatformServices->setApplicationBadge(number); +} } QT_END_NAMESPACE diff --git a/src/client/qwaylandintegration_p.h b/src/client/qwaylandintegration_p.h index ff70ae25d..f26cc3d29 100644 --- a/src/client/qwaylandintegration_p.h +++ b/src/client/qwaylandintegration_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPLATFORMINTEGRATION_WAYLAND_H #define QPLATFORMINTEGRATION_WAYLAND_H @@ -55,6 +19,7 @@ #include <qpa/qplatformintegration.h> #include <QtCore/QScopedPointer> #include <QtCore/QMutex> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -67,14 +32,19 @@ class QWaylandServerBufferIntegration; class QWaylandShellIntegration; class QWaylandInputDeviceIntegration; class QWaylandInputDevice; +class QWaylandScreen; +class QWaylandCursor; +class QWaylandPlatformServices; -class Q_WAYLAND_CLIENT_EXPORT QWaylandIntegration : public QPlatformIntegration +class Q_WAYLANDCLIENT_EXPORT QWaylandIntegration : public QPlatformIntegration { public: QWaylandIntegration(); ~QWaylandIntegration() override; - bool hasFailed() { return mFailed; } + static QWaylandIntegration *instance() { return sInstance; } + + bool init(); bool hasCapability(QPlatformIntegration::Capability cap) const override; QPlatformWindow *createPlatformWindow(QWindow *window) const override; @@ -107,6 +77,8 @@ public: QWaylandDisplay *display() const; + Qt::KeyboardModifiers queryKeyboardModifiers() const override; + QList<int> possibleKeys(const QKeyEvent *event) const override; QStringList themeNames() const override; @@ -117,7 +89,11 @@ public: QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const override; #endif - QWaylandInputDevice *createInputDevice(QWaylandDisplay *display, int version, uint32_t id); + void setApplicationBadge(qint64 number) override; + + virtual QWaylandInputDevice *createInputDevice(QWaylandDisplay *display, int version, uint32_t id) const; + virtual QWaylandScreen *createPlatformScreen(QWaylandDisplay *waylandDisplay, int version, uint32_t id) const; + virtual QWaylandCursor *createPlatformCursor(QWaylandDisplay *display) const; virtual QWaylandClientBufferIntegration *clientBufferIntegration() const; virtual QWaylandServerBufferIntegration *serverBufferIntegration() const; @@ -125,19 +101,25 @@ public: void reconfigureInputContext(); -private: +protected: // NOTE: mDisplay *must* be destructed after mDrag and mClientBufferIntegration // and mShellIntegration. // Do not move this definition into the private section at the bottom. QScopedPointer<QWaylandDisplay> mDisplay; protected: + void reset(); + virtual QPlatformNativeInterface *createPlatformNativeInterface(); + QScopedPointer<QWaylandClientBufferIntegration> mClientBufferIntegration; QScopedPointer<QWaylandServerBufferIntegration> mServerBufferIntegration; QScopedPointer<QWaylandShellIntegration> mShellIntegration; QScopedPointer<QWaylandInputDeviceIntegration> mInputDeviceIntegration; + QScopedPointer<QPlatformInputContext> mInputContext; + private: + void initializePlatform(); void initializeClientBufferIntegration(); void initializeServerBufferIntegration(); void initializeShellIntegration(); @@ -152,16 +134,17 @@ private: QScopedPointer<QPlatformDrag> mDrag; #endif QScopedPointer<QPlatformNativeInterface> mNativeInterface; - QScopedPointer<QPlatformInputContext> mInputContext; #if QT_CONFIG(accessibility) mutable QScopedPointer<QPlatformAccessibility> mAccessibility; #endif - bool mFailed = false; + QScopedPointer<QWaylandPlatformServices> mPlatformServices; QMutex mClientBufferInitLock; bool mClientBufferIntegrationInitialized = false; bool mServerBufferIntegrationInitialized = false; bool mShellIntegrationInitialized = false; + static QWaylandIntegration *sInstance; + friend class QWaylandDisplay; }; diff --git a/src/client/qwaylandnativeinterface.cpp b/src/client/qwaylandnativeinterface.cpp index bf54a1a00..e1586d244 100644 --- a/src/client/qwaylandnativeinterface.cpp +++ b/src/client/qwaylandnativeinterface.cpp @@ -1,53 +1,17 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandnativeinterface_p.h" #include "qwaylanddisplay_p.h" #include "qwaylandwindow_p.h" #include "qwaylandshellintegration_p.h" #include "qwaylandsubsurface_p.h" -#include "qwaylandextendedsurface_p.h" #include "qwaylandintegration_p.h" #include "qwaylanddisplay_p.h" #include "qwaylandwindowmanagerintegration_p.h" #include "qwaylandscreen_p.h" #include "qwaylandinputdevice_p.h" +#include <QtCore/private/qnativeinterface_p.h> #include <QtGui/private/qguiapplication_p.h> #include <QtGui/QScreen> #include <QtWaylandClient/private/qwaylandclientbufferintegration_p.h> @@ -55,8 +19,6 @@ #include <QtWaylandClient/private/qwaylandvulkanwindow_p.h> #endif -#include <QtPlatformHeaders/qwaylandwindowfunctions.h> - QT_BEGIN_NAMESPACE namespace QtWaylandClient { @@ -72,8 +34,10 @@ void *QWaylandNativeInterface::nativeResourceForIntegration(const QByteArray &re if (lowerCaseResource == "display" || lowerCaseResource == "wl_display" || lowerCaseResource == "nativedisplay") return m_integration->display()->wl_display(); - if (lowerCaseResource == "compositor") - return const_cast<wl_compositor *>(m_integration->display()->wl_compositor()); + if (lowerCaseResource == "compositor") { + if (auto compositor = m_integration->display()->compositor()) + return compositor->object(); + } if (lowerCaseResource == "server_buffer_integration") return m_integration->serverBufferIntegration(); @@ -100,7 +64,65 @@ void *QWaylandNativeInterface::nativeResourceForIntegration(const QByteArray &re return touch->wl_touch(); return nullptr; } + if (lowerCaseResource == "serial") + return reinterpret_cast<void *>(quintptr(m_integration->display()->defaultInputDevice()->serial())); + + return nullptr; +} + +wl_display *QtWaylandClient::QWaylandNativeInterface::display() const +{ + return m_integration->display()->wl_display(); +} + +wl_compositor *QtWaylandClient::QWaylandNativeInterface::compositor() const +{ + if (auto compositor = m_integration->display()->compositor()) + return compositor->object(); + return nullptr; +} + +wl_seat *QtWaylandClient::QWaylandNativeInterface::seat() const +{ + if (auto inputDevice = m_integration->display()->defaultInputDevice()) { + return inputDevice->wl_seat(); + } + return nullptr; +} + +wl_keyboard *QtWaylandClient::QWaylandNativeInterface::keyboard() const +{ + if (auto inputDevice = m_integration->display()->defaultInputDevice()) + if (auto keyboard = inputDevice->keyboard()) + return keyboard->wl_keyboard(); + return nullptr; +} + +wl_pointer *QtWaylandClient::QWaylandNativeInterface::pointer() const +{ + if (auto inputDevice = m_integration->display()->defaultInputDevice()) + if (auto pointer = inputDevice->pointer()) + return pointer->wl_pointer(); + return nullptr; +} + +wl_touch *QtWaylandClient::QWaylandNativeInterface::touch() const +{ + if (auto inputDevice = m_integration->display()->defaultInputDevice()) + if (auto touch = inputDevice->touch()) + return touch->wl_touch(); + return nullptr; +} + +uint QtWaylandClient::QWaylandNativeInterface::lastInputSerial() const +{ + return m_integration->display()->lastInputSerial(); +} +wl_seat *QtWaylandClient::QWaylandNativeInterface::lastInputSeat() const +{ + if (auto inputDevice = m_integration->display()->lastInputDevice()) + return inputDevice->wl_seat(); return nullptr; } @@ -110,8 +132,10 @@ void *QWaylandNativeInterface::nativeResourceForWindow(const QByteArray &resourc if (lowerCaseResource == "display") return m_integration->display()->wl_display(); - if (lowerCaseResource == "compositor") - return const_cast<wl_compositor *>(m_integration->display()->wl_compositor()); + if (lowerCaseResource == "compositor") { + if (auto compositor = m_integration->display()->compositor()) + return compositor->object(); + } if (lowerCaseResource == "surface") { QWaylandWindow *w = static_cast<QWaylandWindow*>(window->handle()); return w ? w->wlSurface() : nullptr; @@ -124,13 +148,14 @@ void *QWaylandNativeInterface::nativeResourceForWindow(const QByteArray &resourc if (lowerCaseResource == "vksurface") { if (window->surfaceType() == QSurface::VulkanSurface && window->handle()) { // return a pointer to the VkSurfaceKHR value, not the value itself - return static_cast<QWaylandVulkanWindow *>(window->handle())->surface(); + return static_cast<QWaylandVulkanWindow *>(window->handle())->vkSurface(); } } #endif - if (auto shellIntegration = m_integration->shellIntegration()) - return shellIntegration->nativeResourceForWindow(resourceString, window); + QWaylandWindow *platformWindow = static_cast<QWaylandWindow *>(window->handle()); + if (platformWindow && platformWindow->shellIntegration()) + return platformWindow->shellIntegration()->nativeResourceForWindow(resourceString, window); return nullptr; } @@ -139,7 +164,7 @@ void *QWaylandNativeInterface::nativeResourceForScreen(const QByteArray &resourc { QByteArray lowerCaseResource = resourceString.toLower(); - if (lowerCaseResource == "output") + if (lowerCaseResource == "output" && !screen->handle()->isPlaceholder()) return ((QWaylandScreen *) screen->handle())->output(); return nullptr; @@ -165,6 +190,17 @@ void *QWaylandNativeInterface::nativeResourceForContext(const QByteArray &resour } #endif // opengl +QPlatformNativeInterface::NativeResourceForWindowFunction QWaylandNativeInterface::nativeResourceFunctionForWindow(const QByteArray &resource) +{ + QByteArray lowerCaseResource = resource.toLower(); + + if (lowerCaseResource == "setmargins") { + return NativeResourceForWindowFunction(reinterpret_cast<void *>(setWindowMargins)); + } + + return nullptr; +} + QVariantMap QWaylandNativeInterface::windowProperties(QPlatformWindow *window) const { QWaylandWindow *waylandWindow = static_cast<QWaylandWindow *>(window); @@ -194,42 +230,10 @@ void QWaylandNativeInterface::emitWindowPropertyChanged(QPlatformWindow *window, emit windowPropertyChanged(window,name); } -QFunctionPointer QWaylandNativeInterface::platformFunction(const QByteArray &resource) const +void QWaylandNativeInterface::setWindowMargins(QWindow *window, const QMargins &margins) { - if (resource == QWaylandWindowFunctions::setSyncIdentifier()) { - return QFunctionPointer(setSync); - } else if (resource == QWaylandWindowFunctions::setDeSyncIdentifier()) { - return QFunctionPointer(setDeSync); - } else if (resource == QWaylandWindowFunctions::isSyncIdentifier()) { - return QFunctionPointer(isSync); - } - return nullptr; -} - - -void QWaylandNativeInterface::setSync(QWindow *window) -{ - QWaylandWindow *ww = static_cast<QWaylandWindow*>(window->handle()); - if (ww->subSurfaceWindow()) { - ww->subSurfaceWindow()->setSync(); - } -} - -void QWaylandNativeInterface::setDeSync(QWindow *window) -{ - QWaylandWindow *ww = static_cast<QWaylandWindow*>(window->handle()); - if (ww->subSurfaceWindow()) { - ww->subSurfaceWindow()->setDeSync(); - } -} - -bool QWaylandNativeInterface::isSync(QWindow *window) -{ - QWaylandWindow *ww = static_cast<QWaylandWindow*>(window->handle()); - if (ww->subSurfaceWindow()) { - return ww->subSurfaceWindow()->isSync(); - } - return false; + QWaylandWindow *wlWindow = static_cast<QWaylandWindow*>(window->handle()); + wlWindow->setCustomMargins(margins); } } diff --git a/src/client/qwaylandnativeinterface_p.h b/src/client/qwaylandnativeinterface_p.h index 3de80eb6a..ce8c6bec3 100644 --- a/src/client/qwaylandnativeinterface_p.h +++ b/src/client/qwaylandnativeinterface_p.h @@ -1,44 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QWAYLANDNATIVEINTERFACE_H -#define QWAYLANDNATIVEINTERFACE_H +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWAYLANDNATIVEINTERFACE_P_H +#define QWAYLANDNATIVEINTERFACE_P_H // // W A R N I N G @@ -55,15 +19,21 @@ #include <qpa/qplatformnativeinterface.h> #include <QtWaylandClient/qtwaylandclientglobal.h> +#include <QtCore/private/qglobal_p.h> +#include <QtCore/qhash.h> +#include <QtGui/qguiapplication_platform.h> QT_BEGIN_NAMESPACE +class QMargins; + namespace QtWaylandClient { class QWaylandIntegration; class QWaylandScreen; -class Q_WAYLAND_CLIENT_EXPORT QWaylandNativeInterface : public QPlatformNativeInterface +class Q_WAYLANDCLIENT_EXPORT QWaylandNativeInterface : public QPlatformNativeInterface, + public QNativeInterface::QWaylandApplication { public: QWaylandNativeInterface(QWaylandIntegration *integration); @@ -75,6 +45,7 @@ public: #if QT_CONFIG(opengl) void *nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) override; #endif + NativeResourceForWindowFunction nativeResourceFunctionForWindow(const QByteArray &resource) override; QVariantMap windowProperties(QPlatformWindow *window) const override; QVariant windowProperty(QPlatformWindow *window, const QString &name) const override; QVariant windowProperty(QPlatformWindow *window, const QString &name, const QVariant &defaultValue) const override; @@ -82,19 +53,25 @@ public: void emitWindowPropertyChanged(QPlatformWindow *window, const QString &name); - QFunctionPointer platformFunction(const QByteArray &resource) const override; + // QWaylandApplication interface + wl_display *display() const override; + wl_compositor *compositor() const override; + wl_seat *seat() const override; + wl_keyboard *keyboard() const override; + wl_pointer *pointer() const override; + wl_touch *touch() const override; + uint lastInputSerial() const override; + wl_seat *lastInputSeat() const override; private: + static void setWindowMargins(QWindow *window, const QMargins &margins); + QWaylandIntegration *m_integration = nullptr; QHash<QPlatformWindow*, QVariantMap> m_windowProperties; - - static void setSync(QWindow *window); - static void setDeSync(QWindow *window); - static bool isSync(QWindow *window); }; } QT_END_NAMESPACE -#endif // QWAYLANDNATIVEINTERFACE_H +#endif // QWAYLANDNATIVEINTERFACE_P_H diff --git a/src/client/qwaylandplatformservices.cpp b/src/client/qwaylandplatformservices.cpp new file mode 100644 index 000000000..14556d282 --- /dev/null +++ b/src/client/qwaylandplatformservices.cpp @@ -0,0 +1,50 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwaylandplatformservices_p.h" +#include "qwaylandwindow_p.h" +#include "qwaylanddisplay_p.h" +#include "qwaylandshellsurface_p.h" +#include "qwaylandwindowmanagerintegration_p.h" + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +QWaylandPlatformServices::QWaylandPlatformServices(QWaylandDisplay *display) + : m_display(display) { } + +bool QWaylandPlatformServices::openUrl(const QUrl &url) +{ + if (auto windowManagerIntegration = m_display->windowManagerIntegration()) { + windowManagerIntegration->openUrl(url); + return true; + } + return QGenericUnixServices::openUrl(url); +} + +bool QWaylandPlatformServices::openDocument(const QUrl &url) +{ + if (auto windowManagerIntegration = m_display->windowManagerIntegration()) { + windowManagerIntegration->openUrl(url); + return true; + } + return QGenericUnixServices::openDocument(url); +} + +QString QWaylandPlatformServices::portalWindowIdentifier(QWindow *window) +{ + if (window && window->handle()) { + auto shellSurface = static_cast<QWaylandWindow *>(window->handle())->shellSurface(); + if (shellSurface) { + const QString handle = shellSurface->externWindowHandle(); + return QLatin1String("wayland:") + handle; + } + } + return QString(); +} +} // namespace QtWaylandClient + +QT_END_NAMESPACE + +#include "moc_qwaylandplatformservices_p.cpp" diff --git a/src/client/qwaylandplatformservices_p.h b/src/client/qwaylandplatformservices_p.h new file mode 100644 index 000000000..6106a9018 --- /dev/null +++ b/src/client/qwaylandplatformservices_p.h @@ -0,0 +1,48 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWAYLANDPLATFORMSERVICES_H +#define QWAYLANDPLATFORMSERVICES_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/QObject> + +#include <QtGui/private/qgenericunixservices_p.h> + +#include <QtWaylandClient/private/qwayland-qt-windowmanager.h> +#include <QtWaylandClient/qtwaylandclientglobal.h> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class QWaylandDisplay; + +class Q_WAYLANDCLIENT_EXPORT QWaylandPlatformServices : public QGenericUnixServices +{ +public: + explicit QWaylandPlatformServices(QWaylandDisplay *waylandDisplay); + + bool openUrl(const QUrl &url) override; + bool openDocument(const QUrl &url) override; + QString portalWindowIdentifier(QWindow *window) override; + +private: + QWaylandDisplay *m_display; +}; + +QT_END_NAMESPACE + +} // namespace QtWaylandClient + +#endif // QWAYLANDPLATFORMSERVICES_H diff --git a/src/client/qwaylandpointergestures.cpp b/src/client/qwaylandpointergestures.cpp new file mode 100644 index 000000000..87079d800 --- /dev/null +++ b/src/client/qwaylandpointergestures.cpp @@ -0,0 +1,211 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwaylandpointergestures_p.h" +#include "qwaylanddisplay_p.h" +#include "qwaylandinputdevice_p.h" + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +QWaylandPointerGestures::QWaylandPointerGestures(QWaylandDisplay *display, uint id, uint version) + : zwp_pointer_gestures_v1(display->wl_registry(), id, qMin(version, uint(1))) +{ +} + +QWaylandPointerGestures::~QWaylandPointerGestures() noexcept +{ + if (version() >= ZWP_POINTER_GESTURES_V1_RELEASE_SINCE_VERSION) + release(); + else + zwp_pointer_gestures_v1_destroy(object()); +} + +QWaylandPointerGestureSwipe * + QWaylandPointerGestures::createPointerGestureSwipe(QWaylandInputDevice *device) +{ + return new QWaylandPointerGestureSwipe(device); +} + +QWaylandPointerGesturePinch * + QWaylandPointerGestures::createPointerGesturePinch(QWaylandInputDevice *device) +{ + return new QWaylandPointerGesturePinch(device); +} + +QWaylandPointerGestureSwipe::QWaylandPointerGestureSwipe(QWaylandInputDevice *p) + : mParent(p) +{ +} + +QWaylandPointerGestureSwipe::~QWaylandPointerGestureSwipe() +{ + destroy(); +} + +void QWaylandPointerGestureSwipe::zwp_pointer_gesture_swipe_v1_begin(uint32_t serial, uint32_t time, + struct ::wl_surface *surface, + uint32_t fingers) +{ +#ifndef QT_NO_GESTURES + mFocus = QWaylandWindow::fromWlSurface(surface); + if (!mFocus) { + return; + } + mParent->mSerial = serial; + mFingers = fingers; + + const auto* pointer = mParent->pointer(); + + qCDebug(lcQpaWaylandInput) << "zwp_pointer_gesture_swipe_v1_begin @ " + << pointer->mSurfacePos << "fingers" << fingers; + + auto e = QWaylandPointerGestureSwipeEvent(mFocus, Qt::GestureStarted, time, + pointer->mSurfacePos, pointer->mGlobalPos, mFingers, + QPointF()); + + mFocus->handleSwipeGesture(mParent, e); +#endif +} + +void QWaylandPointerGestureSwipe::zwp_pointer_gesture_swipe_v1_update(uint32_t time, + wl_fixed_t dx, wl_fixed_t dy) +{ +#ifndef QT_NO_GESTURES + if (!mFocus) { + return; + } + const auto* pointer = mParent->pointer(); + + const QPointF delta = QPointF(wl_fixed_to_double(dx), wl_fixed_to_double(dy)); + qCDebug(lcQpaWaylandInput) << "zwp_pointer_gesture_swipe_v1_update @ " + << pointer->mSurfacePos << "delta" << delta; + + auto e = QWaylandPointerGestureSwipeEvent(mFocus, Qt::GestureUpdated, time, + pointer->mSurfacePos, pointer->mGlobalPos, mFingers, delta); + + mFocus->handleSwipeGesture(mParent, e); +#endif +} + +void QWaylandPointerGestureSwipe::zwp_pointer_gesture_swipe_v1_end(uint32_t serial, uint32_t time, + int32_t cancelled) +{ +#ifndef QT_NO_GESTURES + if (!mFocus) { + return; + } + mParent->mSerial = serial; + const auto* pointer = mParent->pointer(); + + qCDebug(lcQpaWaylandInput) << "zwp_pointer_gesture_swipe_v1_end @ " + << pointer->mSurfacePos << (cancelled ? "CANCELED" : ""); + + auto gestureType = cancelled ? Qt::GestureFinished : Qt::GestureCanceled; + + auto e = QWaylandPointerGestureSwipeEvent(mFocus, gestureType, time, + pointer->mSurfacePos, pointer->mGlobalPos, mFingers, + QPointF()); + + mFocus->handleSwipeGesture(mParent, e); + + mFocus.clear(); + mFingers = 0; +#endif +} + +QWaylandPointerGesturePinch::QWaylandPointerGesturePinch(QWaylandInputDevice *p) + : mParent(p) +{ +} + +QWaylandPointerGesturePinch::~QWaylandPointerGesturePinch() +{ + destroy(); +} + +void QWaylandPointerGesturePinch::zwp_pointer_gesture_pinch_v1_begin(uint32_t serial, uint32_t time, + struct ::wl_surface *surface, + uint32_t fingers) +{ +#ifndef QT_NO_GESTURES + mFocus = QWaylandWindow::fromWlSurface(surface); + if (!mFocus) { + return; + } + mParent->mSerial = serial; + mFingers = fingers; + mLastScale = 1; + const auto* pointer = mParent->pointer(); + + qCDebug(lcQpaWaylandInput) << "zwp_pointer_gesture_pinch_v1_begin @ " + << pointer->mSurfacePos << "fingers" << fingers; + + auto e = QWaylandPointerGesturePinchEvent(mFocus, Qt::GestureStarted, time, + pointer->mSurfacePos, pointer->mGlobalPos, mFingers, + QPointF(), 0, 0); + + mFocus->handlePinchGesture(mParent, e); +#endif +} + +void QWaylandPointerGesturePinch::zwp_pointer_gesture_pinch_v1_update(uint32_t time, + wl_fixed_t dx, wl_fixed_t dy, + wl_fixed_t scale, + wl_fixed_t rotation) +{ +#ifndef QT_NO_GESTURES + if (!mFocus) { + return; + } + const auto* pointer = mParent->pointer(); + + const qreal rscale = wl_fixed_to_double(scale); + const qreal rot = wl_fixed_to_double(rotation); + const QPointF delta = QPointF(wl_fixed_to_double(dx), wl_fixed_to_double(dy)); + qCDebug(lcQpaWaylandInput) << "zwp_pointer_gesture_pinch_v1_update @ " + << pointer->mSurfacePos << "delta" << delta + << "scale" << mLastScale << "->" << rscale + << "delta" << rscale - mLastScale << "rot" << rot; + + auto e = QWaylandPointerGesturePinchEvent(mFocus, Qt::GestureUpdated, time, + pointer->mSurfacePos, pointer->mGlobalPos, mFingers, + delta, rscale - mLastScale, rot); + + mFocus->handlePinchGesture(mParent, e); + + mLastScale = rscale; +#endif +} + +void QWaylandPointerGesturePinch::zwp_pointer_gesture_pinch_v1_end(uint32_t serial, uint32_t time, + int32_t cancelled) +{ +#ifndef QT_NO_GESTURES + if (!mFocus) { + return; + } + mParent->mSerial = serial; + const auto* pointer = mParent->pointer(); + + qCDebug(lcQpaWaylandInput) << "zwp_pointer_gesture_swipe_v1_end @ " + << pointer->mSurfacePos << (cancelled ? "CANCELED" : ""); + + auto gestureType = cancelled ? Qt::GestureFinished : Qt::GestureCanceled; + + auto e = QWaylandPointerGesturePinchEvent(mFocus, gestureType, time, + pointer->mSurfacePos, pointer->mGlobalPos, mFingers, + QPointF(), 0, 0); + + mFocus->handlePinchGesture(mParent, e); + + mFocus.clear(); + mFingers = 0; + mLastScale = 1; +#endif +} + +} // namespace QtWaylandClient + +QT_END_NAMESPACE diff --git a/src/client/qwaylandpointergestures_p.h b/src/client/qwaylandpointergestures_p.h new file mode 100644 index 000000000..06ee4a6ed --- /dev/null +++ b/src/client/qwaylandpointergestures_p.h @@ -0,0 +1,115 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWAYLANDPOINTERGESTURES_P_H +#define QWAYLANDPOINTERGESTURES_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtWaylandClient/private/qwayland-pointer-gestures-unstable-v1.h> + +#include <QtWaylandClient/private/qtwaylandclientglobal_p.h> + +#include <QtCore/QObject> +#include <QtCore/QPointer> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class QWaylandDisplay; +class QWaylandWindow; +class QWaylandInputDevice; +class QWaylandPointerGestureSwipe; +class QWaylandPointerGesturePinch; + +class Q_WAYLANDCLIENT_EXPORT QWaylandPointerGestures : public QtWayland::zwp_pointer_gestures_v1 +{ +public: + explicit QWaylandPointerGestures(QWaylandDisplay *display, uint id, uint version); + ~QWaylandPointerGestures(); + + QWaylandPointerGestureSwipe *createPointerGestureSwipe(QWaylandInputDevice *device); + QWaylandPointerGesturePinch *createPointerGesturePinch(QWaylandInputDevice *device); +}; + +class Q_WAYLANDCLIENT_EXPORT QWaylandPointerGestureSwipe : + public QtWayland::zwp_pointer_gesture_swipe_v1 +{ +public: + QWaylandPointerGestureSwipe(QWaylandInputDevice *p); + ~QWaylandPointerGestureSwipe() override; + + void zwp_pointer_gesture_swipe_v1_begin(uint32_t serial, + uint32_t time, + struct ::wl_surface *surface, + uint32_t fingers) override; + + void zwp_pointer_gesture_swipe_v1_update(uint32_t time, + wl_fixed_t dx, + wl_fixed_t dy) override; + + void zwp_pointer_gesture_swipe_v1_end(uint32_t serial, + uint32_t time, + int32_t cancelled) override; + + struct ::zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1() + { + return QtWayland::zwp_pointer_gesture_swipe_v1::object(); + } + + QWaylandInputDevice *mParent = nullptr; + QPointer<QWaylandWindow> mFocus; + uint mFingers = 0; +}; + +class Q_WAYLANDCLIENT_EXPORT QWaylandPointerGesturePinch : + public QtWayland::zwp_pointer_gesture_pinch_v1 +{ +public: + QWaylandPointerGesturePinch(QWaylandInputDevice *p); + ~QWaylandPointerGesturePinch() override; + + void zwp_pointer_gesture_pinch_v1_begin(uint32_t serial, + uint32_t time, + struct ::wl_surface *surface, + uint32_t fingers) override; + + void zwp_pointer_gesture_pinch_v1_update(uint32_t time, + wl_fixed_t dx, + wl_fixed_t dy, + wl_fixed_t scale, + wl_fixed_t rotation) override; + + void zwp_pointer_gesture_pinch_v1_end(uint32_t serial, + uint32_t time, + int32_t cancelled) override; + + struct ::zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1() + { + return QtWayland::zwp_pointer_gesture_pinch_v1::object(); + } + + QWaylandInputDevice *mParent = nullptr; + QPointer<QWaylandWindow> mFocus; + uint mFingers = 0; + + // We need to convert between absolute scale provided by wayland/libinput and zoom deltas + // that Qt expects. This stores the scale of the last pinch event or 1.0 if there was none. + qreal mLastScale = 1; +}; + +} // namespace QtWaylandClient + +QT_END_NAMESPACE + +#endif // QWAYLANDPOINTERGESTURES_P_H diff --git a/src/client/qwaylandprimaryselectionv1.cpp b/src/client/qwaylandprimaryselectionv1.cpp index 832f96780..d72b8f749 100644 --- a/src/client/qwaylandprimaryselectionv1.cpp +++ b/src/client/qwaylandprimaryselectionv1.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandprimaryselectionv1_p.h" #include "qwaylandinputdevice_p.h" @@ -54,11 +18,11 @@ QWaylandPrimarySelectionDeviceManagerV1::QWaylandPrimarySelectionDeviceManagerV1 : zwp_primary_selection_device_manager_v1(display->wl_registry(), id, qMin(version, uint(1))) , m_display(display) { - // Create devices for all seats. - // This only works if we get the global before all devices - const auto seats = m_display->inputDevices(); - for (auto *seat : seats) - seat->setPrimarySelectionDevice(createDevice(seat)); +} + +QWaylandPrimarySelectionDeviceManagerV1::~QWaylandPrimarySelectionDeviceManagerV1() +{ + destroy(); } QWaylandPrimarySelectionDeviceV1 *QWaylandPrimarySelectionDeviceManagerV1::createDevice(QWaylandInputDevice *seat) @@ -160,7 +124,8 @@ void QWaylandPrimarySelectionSourceV1::zwp_primary_selection_source_v1_send(cons action.sa_flags = 0; sigaction(SIGPIPE, &action, &oldAction); - write(fd, content.constData(), size_t(content.size())); + ssize_t unused = write(fd, content.constData(), size_t(content.size())); + Q_UNUSED(unused); sigaction(SIGPIPE, &oldAction, nullptr); } close(fd); @@ -169,3 +134,5 @@ void QWaylandPrimarySelectionSourceV1::zwp_primary_selection_source_v1_send(cons } // namespace QtWaylandClient QT_END_NAMESPACE + +#include "moc_qwaylandprimaryselectionv1_p.cpp" diff --git a/src/client/qwaylandprimaryselectionv1_p.h b/src/client/qwaylandprimaryselectionv1_p.h index 3f0a42a67..f39aec526 100644 --- a/src/client/qwaylandprimaryselectionv1_p.h +++ b/src/client/qwaylandprimaryselectionv1_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDPRIMARYSELECTIONV1_P_H #define QWAYLANDPRIMARYSELECTIONV1_P_H @@ -73,6 +37,7 @@ class QWaylandPrimarySelectionDeviceManagerV1 : public QtWayland::zwp_primary_se { public: explicit QWaylandPrimarySelectionDeviceManagerV1(QWaylandDisplay *display, uint id, uint version); + ~QWaylandPrimarySelectionDeviceManagerV1(); QWaylandPrimarySelectionDeviceV1 *createDevice(QWaylandInputDevice *seat); QWaylandDisplay *display() const { return m_display; } @@ -96,7 +61,7 @@ private: QScopedPointer<QWaylandMimeData> m_mimeData; }; -class Q_WAYLAND_CLIENT_EXPORT QWaylandPrimarySelectionSourceV1 : public QObject, public QtWayland::zwp_primary_selection_source_v1 +class Q_WAYLANDCLIENT_EXPORT QWaylandPrimarySelectionSourceV1 : public QObject, public QtWayland::zwp_primary_selection_source_v1 { Q_OBJECT public: @@ -105,7 +70,7 @@ public: QMimeData *mimeData() const { return m_mimeData; } -signals: +Q_SIGNALS: void cancelled(); protected: @@ -113,7 +78,6 @@ protected: void zwp_primary_selection_source_v1_cancelled() override { emit cancelled(); } private: - QWaylandDisplay *m_display = nullptr; QMimeData *m_mimeData = nullptr; }; diff --git a/src/client/qwaylandqtkey.cpp b/src/client/qwaylandqtkey.cpp index 192619738..079a03e0d 100644 --- a/src/client/qwaylandqtkey.cpp +++ b/src/client/qwaylandqtkey.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandqtkey_p.h" #include "qwaylandinputdevice_p.h" @@ -51,6 +15,11 @@ QWaylandQtKeyExtension::QWaylandQtKeyExtension(QWaylandDisplay *display, uint32_ { } +QWaylandQtKeyExtension::~QWaylandQtKeyExtension() +{ + zqt_key_v1_destroy(object()); +} + void QWaylandQtKeyExtension::zqt_key_v1_key(struct wl_surface *surface, uint32_t time, uint32_t type, diff --git a/src/client/qwaylandqtkey_p.h b/src/client/qwaylandqtkey_p.h index 117c44a6d..1544a0166 100644 --- a/src/client/qwaylandqtkey_p.h +++ b/src/client/qwaylandqtkey_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDQTKEY_H #define QWAYLANDQTKEY_H @@ -55,6 +19,7 @@ #include <QtWaylandClient/qtwaylandclientglobal.h> #include <QtWaylandClient/private/qwayland-qt-key-unstable-v1.h> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -62,10 +27,11 @@ namespace QtWaylandClient { class QWaylandDisplay; -class Q_WAYLAND_CLIENT_EXPORT QWaylandQtKeyExtension : public QtWayland::zqt_key_v1 +class Q_WAYLANDCLIENT_EXPORT QWaylandQtKeyExtension : public QtWayland::zqt_key_v1 { public: QWaylandQtKeyExtension(QWaylandDisplay *display, uint32_t id); + ~QWaylandQtKeyExtension(); private: QWaylandDisplay *m_display = nullptr; diff --git a/src/client/qwaylandscreen.cpp b/src/client/qwaylandscreen.cpp index e70796832..d51e8c6a0 100644 --- a/src/client/qwaylandscreen.cpp +++ b/src/client/qwaylandscreen.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandscreen_p.h" @@ -53,8 +17,18 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { +QWaylandXdgOutputManagerV1::QWaylandXdgOutputManagerV1(QWaylandDisplay* display, uint id, uint version) + : QtWayland::zxdg_output_manager_v1(display->wl_registry(), id, qMin(3u, version)) +{ +} + +QWaylandXdgOutputManagerV1::~QWaylandXdgOutputManagerV1() +{ + destroy(); +} + QWaylandScreen::QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uint32_t id) - : QtWayland::wl_output(waylandDisplay->wl_registry(), id, qMin(version, 2)) + : QtWayland::wl_output(waylandDisplay->wl_registry(), id, qMin(version, 3)) , m_outputId(id) , mWaylandDisplay(waylandDisplay) , mOutputName(QStringLiteral("Screen%1").arg(id)) @@ -66,7 +40,7 @@ QWaylandScreen::QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uin qCWarning(lcQpaWayland) << "wl_output done event not supported by compositor," << "QScreen may not work correctly"; mWaylandDisplay->forceRoundTrip(); // Give the compositor a chance to send geometry etc. - mOutputDone = true; // Fake the done event + mProcessedEvents |= OutputDoneEvent; // Fake the done event maybeInitialize(); } } @@ -75,16 +49,32 @@ QWaylandScreen::~QWaylandScreen() { if (zxdg_output_v1::isInitialized()) zxdg_output_v1::destroy(); + if (wl_output::version() >= WL_OUTPUT_RELEASE_SINCE_VERSION) + wl_output::release(); + else + wl_output_destroy(wl_output::object()); +} + +uint QWaylandScreen::requiredEvents() const +{ + uint ret = OutputDoneEvent; + + if (mWaylandDisplay->xdgOutputManager()) { + if (mWaylandDisplay->xdgOutputManager()->version() >= 2) + ret |= XdgOutputNameEvent; + + if (mWaylandDisplay->xdgOutputManager()->version() < 3) + ret |= XdgOutputDoneEvent; + } + return ret; } void QWaylandScreen::maybeInitialize() { Q_ASSERT(!mInitialized); - if (!mOutputDone) - return; - - if (mWaylandDisplay->xdgOutputManager() && !mXdgOutputDone) + const uint requiredEvents = this->requiredEvents(); + if ((mProcessedEvents & requiredEvents) != requiredEvents) return; mInitialized = true; @@ -95,7 +85,7 @@ void QWaylandScreen::maybeInitialize() updateXdgOutputProperties(); } -void QWaylandScreen::initXdgOutput(QtWayland::zxdg_output_manager_v1 *xdgOutputManager) +void QWaylandScreen::initXdgOutput(QWaylandXdgOutputManagerV1 *xdgOutputManager) { Q_ASSERT(xdgOutputManager); if (zxdg_output_v1::isInitialized()) @@ -165,22 +155,29 @@ QList<QPlatformScreen *> QWaylandScreen::virtualSiblings() const { QList<QPlatformScreen *> list; const QList<QWaylandScreen*> screens = mWaylandDisplay->screens(); - list.reserve(screens.count()); - for (QWaylandScreen *screen : qAsConst(screens)) { + auto *placeholder = mWaylandDisplay->placeholderScreen(); + + list.reserve(screens.size() + (placeholder ? 1 : 0)); + + for (QWaylandScreen *screen : std::as_const(screens)) { if (screen->screen()) list << screen; } + + if (placeholder) + list << placeholder; + return list; } -void QWaylandScreen::setOrientationUpdateMask(Qt::ScreenOrientations mask) +QWindow *QWaylandScreen::topLevelAt(const QPoint & pos) const { - const auto allWindows = QGuiApplication::allWindows(); - for (QWindow *window : allWindows) { - QWaylandWindow *w = static_cast<QWaylandWindow *>(window->handle()); - if (w && w->waylandScreen() == this) - w->setOrientationMask(mask); + if (QWaylandWindow::fixedToplevelPositions) { + Q_UNUSED(pos); + return nullptr; } + + return QPlatformScreen::topLevelAt(pos); } Qt::ScreenOrientation QWaylandScreen::orientation() const @@ -210,9 +207,37 @@ QPlatformCursor *QWaylandScreen::cursor() const } #endif // QT_CONFIG(cursor) -QWaylandScreen * QWaylandScreen::waylandScreenFromWindow(QWindow *window) +QPlatformScreen::SubpixelAntialiasingType QWaylandScreen::subpixelAntialiasingTypeHint() const +{ + QPlatformScreen::SubpixelAntialiasingType type = QPlatformScreen::subpixelAntialiasingTypeHint(); + if (type == QPlatformScreen::Subpixel_None) { + switch (mSubpixel) { + case wl_output::subpixel_unknown: + case wl_output::subpixel_none: + type = QPlatformScreen::Subpixel_None; + break; + case wl_output::subpixel_horizontal_rgb: + type = QPlatformScreen::Subpixel_RGB; + break; + case wl_output::subpixel_horizontal_bgr: + type = QPlatformScreen::Subpixel_BGR; + break; + case wl_output::subpixel_vertical_rgb: + type = QPlatformScreen::Subpixel_VRGB; + break; + case wl_output::subpixel_vertical_bgr: + type = QPlatformScreen::Subpixel_VBGR; + break; + } + } + return type; +} + +QWaylandScreen *QWaylandScreen::waylandScreenFromWindow(QWindow *window) { QPlatformScreen *platformScreen = QPlatformScreen::platformScreenForWindow(window); + if (platformScreen->isPlaceholder()) + return nullptr; return static_cast<QWaylandScreen *>(platformScreen); } @@ -223,6 +248,35 @@ QWaylandScreen *QWaylandScreen::fromWlOutput(::wl_output *output) return nullptr; } +Qt::ScreenOrientation QWaylandScreen::toScreenOrientation(int wlTransform, + Qt::ScreenOrientation fallback) const +{ + auto orientation = fallback; + bool isPortrait = mGeometry.height() > mGeometry.width(); + switch (wlTransform) { + case WL_OUTPUT_TRANSFORM_NORMAL: + orientation = isPortrait ? Qt::PortraitOrientation : Qt::LandscapeOrientation; + break; + case WL_OUTPUT_TRANSFORM_90: + orientation = isPortrait ? Qt::InvertedLandscapeOrientation : Qt::PortraitOrientation; + break; + case WL_OUTPUT_TRANSFORM_180: + orientation = isPortrait ? Qt::InvertedPortraitOrientation : Qt::InvertedLandscapeOrientation; + break; + case WL_OUTPUT_TRANSFORM_270: + orientation = isPortrait ? Qt::LandscapeOrientation : Qt::InvertedPortraitOrientation; + break; + // Ignore these ones, at least for now + case WL_OUTPUT_TRANSFORM_FLIPPED: + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + break; + } + + return orientation; +} + void QWaylandScreen::output_mode(uint32_t flags, int width, int height, int refresh) { if (!(flags & WL_OUTPUT_MODE_CURRENT)) @@ -243,11 +297,10 @@ void QWaylandScreen::output_geometry(int32_t x, int32_t y, const QString &model, int32_t transform) { - Q_UNUSED(subpixel); - mManufacturer = make; mModel = model; + mSubpixel = subpixel; mTransform = transform; mPhysicalSize = QSize(width, height); @@ -261,39 +314,25 @@ void QWaylandScreen::output_scale(int32_t factor) void QWaylandScreen::output_done() { - mOutputDone = true; - if (mInitialized) + mProcessedEvents |= OutputDoneEvent; + + if (mInitialized) { updateOutputProperties(); - else + if (zxdg_output_v1::isInitialized()) + updateXdgOutputProperties(); + } else { maybeInitialize(); + } } void QWaylandScreen::updateOutputProperties() { if (mTransform >= 0) { - bool isPortrait = mGeometry.height() > mGeometry.width(); - switch (mTransform) { - case WL_OUTPUT_TRANSFORM_NORMAL: - m_orientation = isPortrait ? Qt::PortraitOrientation : Qt::LandscapeOrientation; - break; - case WL_OUTPUT_TRANSFORM_90: - m_orientation = isPortrait ? Qt::InvertedLandscapeOrientation : Qt::PortraitOrientation; - break; - case WL_OUTPUT_TRANSFORM_180: - m_orientation = isPortrait ? Qt::InvertedPortraitOrientation : Qt::InvertedLandscapeOrientation; - break; - case WL_OUTPUT_TRANSFORM_270: - m_orientation = isPortrait ? Qt::LandscapeOrientation : Qt::InvertedPortraitOrientation; - break; - // Ignore these ones, at least for now - case WL_OUTPUT_TRANSFORM_FLIPPED: - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - break; + auto newOrientation = toScreenOrientation(mTransform, m_orientation); + if (m_orientation != newOrientation) { + m_orientation = newOrientation; + QWindowSystemInterface::handleScreenOrientationChange(screen(), m_orientation); } - - QWindowSystemInterface::handleScreenOrientationChange(screen(), m_orientation); mTransform = -1; } @@ -316,7 +355,10 @@ void QWaylandScreen::zxdg_output_v1_logical_size(int32_t width, int32_t height) void QWaylandScreen::zxdg_output_v1_done() { - mXdgOutputDone = true; + if (Q_UNLIKELY(mWaylandDisplay->xdgOutputManager()->version() >= 3)) + qWarning(lcQpaWayland) << "zxdg_output_v1.done received on version 3 or newer, this is most likely a bug in the compositor"; + + mProcessedEvents |= XdgOutputDoneEvent; if (mInitialized) updateXdgOutputProperties(); else @@ -325,7 +367,11 @@ void QWaylandScreen::zxdg_output_v1_done() void QWaylandScreen::zxdg_output_v1_name(const QString &name) { + if (Q_UNLIKELY(mInitialized)) + qWarning(lcQpaWayland) << "zxdg_output_v1.name received after output has been initialized, this is most likely a bug in the compositor"; + mOutputName = name; + mProcessedEvents |= XdgOutputNameEvent; } void QWaylandScreen::updateXdgOutputProperties() diff --git a/src/client/qwaylandscreen_p.h b/src/client/qwaylandscreen_p.h index e9e07d9cd..ce3df3099 100644 --- a/src/client/qwaylandscreen_p.h +++ b/src/client/qwaylandscreen_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDSCREEN_H #define QWAYLANDSCREEN_H @@ -52,11 +16,12 @@ // #include <qpa/qplatformscreen.h> +#include <QtGui/qscreen_platform.h> #include <QtWaylandClient/qtwaylandclientglobal.h> #include <QtWaylandClient/private/qwayland-wayland.h> #include <QtWaylandClient/private/qwayland-xdg-output-unstable-v1.h> - +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -65,7 +30,16 @@ namespace QtWaylandClient { class QWaylandDisplay; class QWaylandCursor; -class Q_WAYLAND_CLIENT_EXPORT QWaylandScreen : public QPlatformScreen, QtWayland::wl_output, QtWayland::zxdg_output_v1 +class Q_WAYLANDCLIENT_EXPORT QWaylandXdgOutputManagerV1 : public QtWayland::zxdg_output_manager_v1 { +public: + QWaylandXdgOutputManagerV1(QWaylandDisplay *display, uint id, uint version); + ~QWaylandXdgOutputManagerV1(); +}; + +class Q_WAYLANDCLIENT_EXPORT QWaylandScreen : public QPlatformScreen, + QtWayland::wl_output, + QtWayland::zxdg_output_v1, + public QNativeInterface::QWaylandScreen { public: QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uint32_t id); @@ -73,7 +47,7 @@ public: void maybeInitialize(); - void initXdgOutput(QtWayland::zxdg_output_manager_v1 *xdgOutputManager); + void initXdgOutput(QWaylandXdgOutputManagerV1 *xdgOutputManager); QWaylandDisplay *display() const; @@ -89,7 +63,7 @@ public: QDpi logicalDpi() const override; QList<QPlatformScreen *> virtualSiblings() const override; - void setOrientationUpdateMask(Qt::ScreenOrientations mask) override; + QWindow *topLevelAt(const QPoint &point) const override; Qt::ScreenOrientation orientation() const override; int scale() const; @@ -102,13 +76,28 @@ public: QPlatformCursor *cursor() const override; #endif + SubpixelAntialiasingType subpixelAntialiasingTypeHint() const override; + uint32_t outputId() const { return m_outputId; } - ::wl_output *output() { return QtWayland::wl_output::object(); } + ::wl_output *output() const override + { + return const_cast<::wl_output *>(QtWayland::wl_output::object()); + } static QWaylandScreen *waylandScreenFromWindow(QWindow *window); static QWaylandScreen *fromWlOutput(::wl_output *output); -private: + Qt::ScreenOrientation toScreenOrientation(int wlTransform, + Qt::ScreenOrientation fallback) const; + +protected: + enum Event : uint { + XdgOutputDoneEvent = 0x1, + OutputDoneEvent = 0x2, + XdgOutputNameEvent = 0x4, + }; + uint requiredEvents() const; + void output_mode(uint32_t flags, int width, int height, int refresh) override; void output_geometry(int32_t x, int32_t y, int32_t width, int32_t height, @@ -136,18 +125,14 @@ private: int mScale = 1; int mDepth = 32; int mRefreshRate = 60000; + int mSubpixel = -1; int mTransform = -1; QImage::Format mFormat = QImage::Format_ARGB32_Premultiplied; QSize mPhysicalSize; QString mOutputName; Qt::ScreenOrientation m_orientation = Qt::PrimaryOrientation; - bool mOutputDone = false; - bool mXdgOutputDone = false; + uint mProcessedEvents = 0; bool mInitialized = false; - -#if QT_CONFIG(cursor) - QScopedPointer<QWaylandCursor> mWaylandCursor; -#endif }; } diff --git a/src/client/qwaylandshellsurface.cpp b/src/client/qwaylandshellsurface.cpp index 91cc2c2c0..fde1e1d3f 100644 --- a/src/client/qwaylandshellsurface.cpp +++ b/src/client/qwaylandshellsurface.cpp @@ -1,45 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the config.tests of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandshellsurface_p.h" #include "qwaylandwindow_p.h" -#include "qwaylandextendedsurface_p.h" +#include "qwaylandinputdevice_p.h" QT_BEGIN_NAMESPACE @@ -57,10 +21,66 @@ void QWaylandShellSurface::setWindowFlags(Qt::WindowFlags flags) void QWaylandShellSurface::sendProperty(const QString &name, const QVariant &value) { - Q_UNUSED(name) - Q_UNUSED(value) + Q_UNUSED(name); + Q_UNUSED(value); } +QPlatformWindow *QWaylandShellSurface::platformWindow() +{ + return m_window; +} + +wl_surface *QWaylandShellSurface::wlSurface() +{ + return m_window ? m_window->wlSurface() : nullptr; +} + +void QWaylandShellSurface::resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset) +{ + m_window->resizeFromApplyConfigure(sizeWithMargins, offset); +} + +void QWaylandShellSurface::repositionFromApplyConfigure(const QPoint &position) +{ + m_window->repositionFromApplyConfigure(position); +} + +void QWaylandShellSurface::setGeometryFromApplyConfigure(const QPoint &globalPosition, const QSize &sizeWithMargins) +{ + m_window->setGeometryFromApplyConfigure(globalPosition, sizeWithMargins); +} + +void QWaylandShellSurface::applyConfigureWhenPossible() +{ + m_window->applyConfigureWhenPossible(); +} + +void QWaylandShellSurface::handleActivationChanged(bool activated) +{ + if (activated) + m_window->display()->handleWindowActivated(m_window); + else + m_window->display()->handleWindowDeactivated(m_window); +} + +uint32_t QWaylandShellSurface::getSerial(QWaylandInputDevice *inputDevice) +{ + return inputDevice->serial(); +} + +void QWaylandShellSurface::setXdgActivationToken(const QString &token) +{ + Q_UNUSED(token); + qCWarning(lcQpaWayland) << "setXdgActivationToken not implemented" << token; +} + +void QWaylandShellSurface::requestXdgActivationToken(quint32 serial) +{ + Q_UNUSED(serial); + Q_EMIT m_window->xdgActivationTokenCreated({}); +} } QT_END_NAMESPACE + +#include "moc_qwaylandshellsurface_p.cpp" diff --git a/src/client/qwaylandshellsurface_p.h b/src/client/qwaylandshellsurface_p.h index f5f202d08..8632efd04 100644 --- a/src/client/qwaylandshellsurface_p.h +++ b/src/client/qwaylandshellsurface_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the config.tests of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDSHELLSURFACE_H #define QWAYLANDSHELLSURFACE_H @@ -53,28 +17,32 @@ #include <QtCore/QSize> #include <QObject> - -#include <QtWaylandClient/private/qwayland-wayland.h> +#include <QPoint> #include <QtWaylandClient/qtwaylandclientglobal.h> +#include <QtCore/private/qglobal_p.h> + +#include <any> + +struct wl_surface; QT_BEGIN_NAMESPACE class QVariant; class QWindow; +class QPlatformWindow; namespace QtWaylandClient { class QWaylandWindow; class QWaylandInputDevice; -class Q_WAYLAND_CLIENT_EXPORT QWaylandShellSurface : public QObject +class Q_WAYLANDCLIENT_EXPORT QWaylandShellSurface : public QObject { Q_OBJECT public: explicit QWaylandShellSurface(QWaylandWindow *window); ~QWaylandShellSurface() override {} - virtual void resize(QWaylandInputDevice * /*inputDevice*/, Qt::Edges /*edges*/) {} - + virtual bool resize(QWaylandInputDevice *, Qt::Edges) { return false; } virtual bool move(QWaylandInputDevice *) { return false; } virtual bool showWindowMenu(QWaylandInputDevice *seat) { Q_UNUSED(seat); return false; } virtual void setTitle(const QString & /*title*/) {} @@ -87,19 +55,47 @@ public: virtual void raise() {} virtual void lower() {} - virtual void setContentOrientationMask(Qt::ScreenOrientations orientation) { Q_UNUSED(orientation) } + virtual void setContentOrientationMask(Qt::ScreenOrientations orientation) { Q_UNUSED(orientation); } virtual void sendProperty(const QString &name, const QVariant &value); - inline QWaylandWindow *window() { return m_window; } - virtual void applyConfigure() {} virtual void requestWindowStates(Qt::WindowStates states) {Q_UNUSED(states);} virtual bool wantsDecorations() const { return false; } + virtual QMargins serverSideFrameMargins() const { return QMargins(); } virtual void propagateSizeHints() {} virtual void setWindowGeometry(const QRect &rect) { Q_UNUSED(rect); } + virtual void setWindowPosition(const QPoint &position) { Q_UNUSED(position); } + + virtual bool requestActivate() { return false; } + virtual bool requestActivateOnShow() { return false; } + virtual void setXdgActivationToken(const QString &token); + virtual void requestXdgActivationToken(quint32 serial); + + virtual void setAlertState(bool enabled) { Q_UNUSED(enabled); } + virtual bool isAlertState() const { return false; } + + virtual QString externWindowHandle() { return QString(); } + + inline QWaylandWindow *window() { return m_window; } + QPlatformWindow *platformWindow(); + struct wl_surface *wlSurface(); + + virtual std::any surfaceRole() const { return std::any(); }; + + virtual void attachPopup(QWaylandShellSurface *popup) { Q_UNUSED(popup); } + virtual void detachPopup(QWaylandShellSurface *popup) { Q_UNUSED(popup); } + +protected: + void resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset = {0, 0}); + void repositionFromApplyConfigure(const QPoint &position); + void setGeometryFromApplyConfigure(const QPoint &globalPosition, const QSize &sizeWithMargins); + void applyConfigureWhenPossible(); + void handleActivationChanged(bool activated); + + static uint32_t getSerial(QWaylandInputDevice *inputDevice); private: QWaylandWindow *m_window = nullptr; diff --git a/src/client/qwaylandshm.cpp b/src/client/qwaylandshm.cpp index e94357bb5..9ee7a96bc 100644 --- a/src/client/qwaylandshm.cpp +++ b/src/client/qwaylandshm.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 LG Electronics Inc, author: <mikko.levonmaa@lge.com> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 LG Electronics Inc, author: <mikko.levonmaa@lge.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtWaylandClient/private/qwaylandshm_p.h> #include <QtWaylandClient/private/qwaylanddisplay_p.h> @@ -52,7 +16,7 @@ QWaylandShm::QWaylandShm(QWaylandDisplay *display, int version, uint32_t id) QWaylandShm::~QWaylandShm() { - + wl_shm_destroy(object()); } void QWaylandShm::shm_format(uint32_t format) diff --git a/src/client/qwaylandshm_p.h b/src/client/qwaylandshm_p.h index 639effa16..eb4a90c12 100644 --- a/src/client/qwaylandshm_p.h +++ b/src/client/qwaylandshm_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 LG Electronics Inc, author: <mikko.levonmaa@lge.com> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 LG Electronics Inc, author: <mikko.levonmaa@lge.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDSHM_H #define QWAYLANDSHM_H @@ -51,11 +15,12 @@ // We mean it. // -#include <QVector> #include <QImage> +#include <QList> #include <QtWaylandClient/qtwaylandclientglobal.h> #include <QtWaylandClient/private/qwayland-wayland.h> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -63,7 +28,7 @@ namespace QtWaylandClient { class QWaylandDisplay; -class Q_WAYLAND_CLIENT_EXPORT QWaylandShm : public QtWayland::wl_shm +class Q_WAYLANDCLIENT_EXPORT QWaylandShm : public QtWayland::wl_shm { public: @@ -81,7 +46,7 @@ protected: void shm_format(uint32_t format) override; private: - QVector<uint32_t> m_formats; + QList<uint32_t> m_formats; }; diff --git a/src/client/qwaylandshmbackingstore.cpp b/src/client/qwaylandshmbackingstore.cpp index 9b5971a21..d77c548a0 100644 --- a/src/client/qwaylandshmbackingstore.cpp +++ b/src/client/qwaylandshmbackingstore.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandshmbackingstore_p.h" #include "qwaylandwindow_p.h" #include "qwaylandsubsurface_p.h" @@ -47,10 +11,12 @@ #include <QtCore/qstandardpaths.h> #include <QtCore/qtemporaryfile.h> #include <QtGui/QPainter> +#include <QtGui/QTransform> #include <QMutexLocker> #include <QtWaylandClient/private/wayland-wayland-client-protocol.h> +#include <fcntl.h> #include <unistd.h> #include <sys/mman.h> @@ -60,6 +26,19 @@ # ifndef MFD_CLOEXEC # define MFD_CLOEXEC 0x0001U # endif +# ifndef MFD_ALLOW_SEALING +# define MFD_ALLOW_SEALING 0x0002U +# endif +// from bits/fcntl-linux.h +# ifndef F_ADD_SEALS +# define F_ADD_SEALS 1033 +# endif +# ifndef F_SEAL_SEAL +# define F_SEAL_SEAL 0x0001 +# endif +# ifndef F_SEAL_SHRINK +# define F_SEAL_SHRINK 0x0002 +# endif #endif QT_BEGIN_NAMESPACE @@ -67,14 +46,17 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { QWaylandShmBuffer::QWaylandShmBuffer(QWaylandDisplay *display, - const QSize &size, QImage::Format format, int scale) + const QSize &size, QImage::Format format, qreal scale) + : mDirtyRegion(QRect(QPoint(0, 0), size / scale)) { int stride = size.width() * 4; int alloc = stride * size.height(); int fd = -1; #ifdef SYS_memfd_create - fd = syscall(SYS_memfd_create, "wayland-shm", MFD_CLOEXEC); + fd = syscall(SYS_memfd_create, "wayland-shm", MFD_CLOEXEC | MFD_ALLOW_SEALING); + if (fd >= 0) + fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); #endif QScopedPointer<QFile> filePointer; @@ -89,6 +71,7 @@ QWaylandShmBuffer::QWaylandShmBuffer(QWaylandDisplay *display, file->open(fd, QIODevice::ReadWrite | QIODevice::Unbuffered, QFile::AutoCloseHandle); filePointer.reset(file); } + // NOTE beginPaint assumes a new buffer be all zeroes, which QFile::resize does. if (!filePointer->isOpen() || !filePointer->resize(alloc)) { qWarning("QWaylandShmBuffer: failed: %s", qUtf8Printable(filePointer->errorString())); return; @@ -107,7 +90,7 @@ QWaylandShmBuffer::QWaylandShmBuffer(QWaylandDisplay *display, QWaylandShm* shm = display->shm(); wl_shm_format wl_format = shm->formatFrom(format); mImage = QImage(data, size.width(), size.height(), stride, format); - mImage.setDevicePixelRatio(qreal(scale)); + mImage.setDevicePixelRatio(scale); mShmPool = wl_shm_create_pool(shm->object(), fd, alloc); init(wl_shm_pool_create_buffer(mShmPool,0, size.width(), size.height(), @@ -125,7 +108,7 @@ QWaylandShmBuffer::~QWaylandShmBuffer(void) QImage *QWaylandShmBuffer::imageInsideMargins(const QMargins &marginsIn) { - QMargins margins = marginsIn * int(mImage.devicePixelRatio()); + QMargins margins = marginsIn * mImage.devicePixelRatio(); if (!margins.isNull() && margins != mMargins) { if (mMarginsImage) { @@ -151,11 +134,22 @@ QImage *QWaylandShmBuffer::imageInsideMargins(const QMargins &marginsIn) } -QWaylandShmBackingStore::QWaylandShmBackingStore(QWindow *window) +QWaylandShmBackingStore::QWaylandShmBackingStore(QWindow *window, QWaylandDisplay *display) : QPlatformBackingStore(window) - , mDisplay(QWaylandScreen::waylandScreenFromWindow(window)->display()) + , mDisplay(display) { - + QObject::connect(mDisplay, &QWaylandDisplay::connected, window, [this]() { + auto copy = mBuffers; + // clear available buffers so we create new ones + // actual deletion is deferred till after resize call so we can copy + // contents from the back buffer + mBuffers.clear(); + mFrontBuffer = nullptr; + // recreateBackBufferIfNeeded always resets mBackBuffer + if (mRequestedSize.isValid() && waylandWindow()) + recreateBackBufferIfNeeded(); + qDeleteAll(copy); + }); } QWaylandShmBackingStore::~QWaylandShmBackingStore() @@ -174,14 +168,29 @@ QPaintDevice *QWaylandShmBackingStore::paintDevice() return contentSurface(); } +void QWaylandShmBackingStore::updateDirtyStates(const QRegion ®ion) +{ + // Update dirty state of buffers based on what was painted. The back buffer will + // not be dirty since we already painted on it, while other buffers will become dirty. + for (QWaylandShmBuffer *b : std::as_const(mBuffers)) { + if (b != mBackBuffer) + b->dirtyRegion() += region; + } +} + void QWaylandShmBackingStore::beginPaint(const QRegion ®ion) { mPainting = true; - ensureSize(); + waylandWindow()->setBackingStore(this); + const bool bufferWasRecreated = recreateBackBufferIfNeeded(); - waylandWindow()->setCanResize(false); + const QMargins margins = windowDecorationMargins(); + updateDirtyStates(region.translated(margins.left(), margins.top())); - if (mBackBuffer->image()->hasAlphaChannel()) { + // Although undocumented, QBackingStore::beginPaint expects the painted region + // to be cleared before use if the window has a surface format with an alpha. + // Fresh QWaylandShmBuffer are already cleared, so we don't need to clear those. + if (!bufferWasRecreated && mBackBuffer->image()->hasAlphaChannel()) { QPainter p(paintDevice()); p.setCompositionMode(QPainter::CompositionMode_Source); const QColor blank = Qt::transparent; @@ -195,14 +204,6 @@ void QWaylandShmBackingStore::endPaint() mPainting = false; if (mPendingFlush) flush(window(), mPendingRegion, QPoint()); - waylandWindow()->setCanResize(true); -} - -void QWaylandShmBackingStore::ensureSize() -{ - waylandWindow()->setBackingStore(this); - waylandWindow()->createDecoration(); - resize(mRequestedSize); } void QWaylandShmBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) @@ -241,8 +242,10 @@ void QWaylandShmBackingStore::resize(const QSize &size, const QRegion &) mRequestedSize = size; } -QWaylandShmBuffer *QWaylandShmBackingStore::getBuffer(const QSize &size) +QWaylandShmBuffer *QWaylandShmBackingStore::getBuffer(const QSize &size, bool &bufferWasRecreated) { + bufferWasRecreated = false; + const auto copy = mBuffers; // remove when ported to vector<unique_ptr> + remove_if for (QWaylandShmBuffer *b : copy) { if (!b->busy()) { @@ -261,40 +264,61 @@ QWaylandShmBuffer *QWaylandShmBackingStore::getBuffer(const QSize &size) if (mBuffers.size() < MAX_BUFFERS) { QImage::Format format = QPlatformScreen::platformScreenForWindow(window())->format(); QWaylandShmBuffer *b = new QWaylandShmBuffer(mDisplay, size, format, waylandWindow()->scale()); + bufferWasRecreated = true; mBuffers.push_front(b); return b; } return nullptr; } -void QWaylandShmBackingStore::resize(const QSize &size) +bool QWaylandShmBackingStore::recreateBackBufferIfNeeded() { + bool bufferWasRecreated = false; QMargins margins = windowDecorationMargins(); - int scale = waylandWindow()->scale(); - QSize sizeWithMargins = (size + QSize(margins.left()+margins.right(),margins.top()+margins.bottom())) * scale; + qreal scale = waylandWindow()->scale(); + const QSize sizeWithMargins = (mRequestedSize + QSize(margins.left() + margins.right(), margins.top() + margins.bottom())) * scale; // We look for a free buffer to draw into. If the buffer is not the last buffer we used, - // that is mBackBuffer, and the size is the same we memcpy the old content into the new + // that is mBackBuffer, and the size is the same we copy the damaged content into the new // buffer so that QPainter is happy to find the stuff it had drawn before. If the new // buffer has a different size it needs to be redrawn completely anyway, and if the buffer // is the same the stuff is there already. // You can exercise the different codepaths with weston, switching between the gl and the // pixman renderer. With the gl renderer release events are sent early so we can effectively // run single buffered, while with the pixman renderer we have to use two. - QWaylandShmBuffer *buffer = getBuffer(sizeWithMargins); + QWaylandShmBuffer *buffer = getBuffer(sizeWithMargins, bufferWasRecreated); while (!buffer) { qCDebug(lcWaylandBackingstore, "QWaylandShmBackingStore: stalling waiting for a buffer to be released from the compositor..."); mDisplay->blockingReadEvents(); - buffer = getBuffer(sizeWithMargins); + buffer = getBuffer(sizeWithMargins, bufferWasRecreated); } qsizetype oldSizeInBytes = mBackBuffer ? mBackBuffer->image()->sizeInBytes() : 0; qsizetype newSizeInBytes = buffer->image()->sizeInBytes(); // mBackBuffer may have been deleted here but if so it means its size was different so we wouldn't copy it anyway - if (mBackBuffer != buffer && oldSizeInBytes == newSizeInBytes) - memcpy(buffer->image()->bits(), mBackBuffer->image()->constBits(), newSizeInBytes); + if (mBackBuffer != buffer && oldSizeInBytes == newSizeInBytes) { + Q_ASSERT(mBackBuffer); + const QImage *sourceImage = mBackBuffer->image(); + QImage *targetImage = buffer->image(); + + QPainter painter(targetImage); + painter.setCompositionMode(QPainter::CompositionMode_Source); + + // Let painter operate in device pixels, to make it easier to compare coordinates + const qreal sourceDevicePixelRatio = sourceImage->devicePixelRatio(); + const qreal targetDevicePixelRatio = painter.device()->devicePixelRatio(); + painter.scale(1.0 / targetDevicePixelRatio, 1.0 / targetDevicePixelRatio); + + for (const QRect &rect : buffer->dirtyRegion()) { + QRectF sourceRect(QPointF(rect.topLeft()) * sourceDevicePixelRatio, + QSizeF(rect.size()) * sourceDevicePixelRatio); + QRectF targetRect(QPointF(rect.topLeft()) * targetDevicePixelRatio, + QSizeF(rect.size()) * targetDevicePixelRatio); + painter.drawImage(targetRect, *sourceImage, sourceRect); + } + } mBackBuffer = buffer; @@ -307,6 +331,10 @@ void QWaylandShmBackingStore::resize(const QSize &size) if (windowDecoration() && window()->isVisible() && oldSizeInBytes != newSizeInBytes) windowDecoration()->update(); + + buffer->dirtyRegion() = QRegion(); + + return bufferWasRecreated; } QImage *QWaylandShmBackingStore::entireSurface() const @@ -328,9 +356,10 @@ void QWaylandShmBackingStore::updateDecorations() qreal dp = sourceImage.devicePixelRatio(); int dpWidth = int(sourceImage.width() / dp); int dpHeight = int(sourceImage.height() / dp); - QMatrix sourceMatrix; + QTransform sourceMatrix; sourceMatrix.scale(dp, dp); QRect target; // needs to be in device independent pixels + QRegion dirtyRegion; //Top target.setX(0); @@ -338,16 +367,19 @@ void QWaylandShmBackingStore::updateDecorations() target.setWidth(dpWidth); target.setHeight(windowDecorationMargins().top()); decorationPainter.drawImage(target, sourceImage, sourceMatrix.mapRect(target)); + dirtyRegion += target; //Left target.setWidth(windowDecorationMargins().left()); target.setHeight(dpHeight); decorationPainter.drawImage(target, sourceImage, sourceMatrix.mapRect(target)); + dirtyRegion += target; //Right target.setX(dpWidth - windowDecorationMargins().right()); target.setWidth(windowDecorationMargins().right()); decorationPainter.drawImage(target, sourceImage, sourceMatrix.mapRect(target)); + dirtyRegion += target; //Bottom target.setX(0); @@ -355,6 +387,9 @@ void QWaylandShmBackingStore::updateDecorations() target.setWidth(dpWidth); target.setHeight(windowDecorationMargins().bottom()); decorationPainter.drawImage(target, sourceImage, sourceMatrix.mapRect(target)); + dirtyRegion += target; + + updateDirtyStates(dirtyRegion); } QWaylandAbstractDecoration *QWaylandShmBackingStore::windowDecoration() const diff --git a/src/client/qwaylandshmbackingstore_p.h b/src/client/qwaylandshmbackingstore_p.h index 8a85cd7f3..6d276bf7b 100644 --- a/src/client/qwaylandshmbackingstore_p.h +++ b/src/client/qwaylandshmbackingstore_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDSHMBACKINGSTORE_H #define QWAYLANDSHMBACKINGSTORE_H @@ -68,33 +32,35 @@ class QWaylandDisplay; class QWaylandAbstractDecoration; class QWaylandWindow; -class Q_WAYLAND_CLIENT_EXPORT QWaylandShmBuffer : public QWaylandBuffer { +class Q_WAYLANDCLIENT_EXPORT QWaylandShmBuffer : public QWaylandBuffer { public: QWaylandShmBuffer(QWaylandDisplay *display, - const QSize &size, QImage::Format format, int scale = 1); + const QSize &size, QImage::Format format, qreal scale = 1); ~QWaylandShmBuffer() override; QSize size() const override { return mImage.size(); } int scale() const override { return int(mImage.devicePixelRatio()); } QImage *image() { return &mImage; } QImage *imageInsideMargins(const QMargins &margins); + + QRegion &dirtyRegion() { return mDirtyRegion; } private: QImage mImage; struct wl_shm_pool *mShmPool = nullptr; QMargins mMargins; QImage *mMarginsImage = nullptr; + QRegion mDirtyRegion; }; -class Q_WAYLAND_CLIENT_EXPORT QWaylandShmBackingStore : public QPlatformBackingStore +class Q_WAYLANDCLIENT_EXPORT QWaylandShmBackingStore : public QPlatformBackingStore { public: - QWaylandShmBackingStore(QWindow *window); + QWaylandShmBackingStore(QWindow *window, QWaylandDisplay *display); ~QWaylandShmBackingStore() override; QPaintDevice *paintDevice() override; void flush(QWindow *window, const QRegion ®ion, const QPoint &offset) override; void resize(const QSize &size, const QRegion &staticContents) override; - void resize(const QSize &size); void beginPaint(const QRegion ®ion) override; void endPaint() override; @@ -103,7 +69,7 @@ public: QMargins windowDecorationMargins() const; QImage *entireSurface() const; QImage *contentSurface() const; - void ensureSize(); + bool recreateBackBufferIfNeeded(); QWaylandWindow *waylandWindow() const; void iterateBuffer(); @@ -113,8 +79,9 @@ public: #endif private: + void updateDirtyStates(const QRegion ®ion); void updateDecorations(); - QWaylandShmBuffer *getBuffer(const QSize &size); + QWaylandShmBuffer *getBuffer(const QSize &size, bool &bufferWasRecreated); QWaylandDisplay *mDisplay = nullptr; std::list<QWaylandShmBuffer *> mBuffers; diff --git a/src/client/qwaylandshmwindow.cpp b/src/client/qwaylandshmwindow.cpp index 52833803d..8fecad178 100644 --- a/src/client/qwaylandshmwindow.cpp +++ b/src/client/qwaylandshmwindow.cpp @@ -1,56 +1,18 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the config.tests of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandshmwindow_p.h" #include "qwaylandbuffer_p.h" -#include <QtCore/QVector> - #include <QtCore/QDebug> QT_BEGIN_NAMESPACE namespace QtWaylandClient { -QWaylandShmWindow::QWaylandShmWindow(QWindow *window) - : QWaylandWindow(window) +QWaylandShmWindow::QWaylandShmWindow(QWindow *window, QWaylandDisplay *display) + : QWaylandWindow(window, display) { } diff --git a/src/client/qwaylandshmwindow_p.h b/src/client/qwaylandshmwindow_p.h index ae1727859..f11df5e47 100644 --- a/src/client/qwaylandshmwindow_p.h +++ b/src/client/qwaylandshmwindow_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the config.tests of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDSHMWINDOW_H #define QWAYLANDSHMWINDOW_H @@ -58,10 +22,10 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { -class Q_WAYLAND_CLIENT_EXPORT QWaylandShmWindow : public QWaylandWindow +class Q_WAYLANDCLIENT_EXPORT QWaylandShmWindow : public QWaylandWindow { public: - QWaylandShmWindow(QWindow *window); + QWaylandShmWindow(QWindow *window, QWaylandDisplay *display); ~QWaylandShmWindow() override; WindowType windowType() const override; diff --git a/src/client/qwaylandsubsurface.cpp b/src/client/qwaylandsubsurface.cpp index 2ff966cbc..a0afd06ed 100644 --- a/src/client/qwaylandsubsurface.cpp +++ b/src/client/qwaylandsubsurface.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandsubsurface_p.h" diff --git a/src/client/qwaylandsubsurface_p.h b/src/client/qwaylandsubsurface_p.h index 76da10b24..7600c5807 100644 --- a/src/client/qwaylandsubsurface_p.h +++ b/src/client/qwaylandsubsurface_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDSUBSURFACE_H #define QWAYLANDSUBSURFACE_H @@ -51,7 +15,7 @@ // We mean it. // -#include <QtCore/qglobal.h> +#include <QtCore/private/qglobal_p.h> #include <QtCore/qmutex.h> #include <QtWaylandClient/qtwaylandclientglobal.h> @@ -64,7 +28,7 @@ namespace QtWaylandClient { class QWaylandDisplay; class QWaylandWindow; -class Q_WAYLAND_CLIENT_EXPORT QWaylandSubSurface : public QtWayland::wl_subsurface +class Q_WAYLANDCLIENT_EXPORT QWaylandSubSurface : public QtWayland::wl_subsurface { public: QWaylandSubSurface(QWaylandWindow *window, QWaylandWindow *parent, ::wl_subsurface *subsurface); diff --git a/src/client/qwaylandsurface.cpp b/src/client/qwaylandsurface.cpp index c35f01b56..274fdda82 100644 --- a/src/client/qwaylandsurface.cpp +++ b/src/client/qwaylandsurface.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the config.tests of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandsurface_p.h" #include "qwaylanddisplay_p.h" @@ -51,6 +15,7 @@ QWaylandSurface::QWaylandSurface(QWaylandDisplay *display) : wl_surface(display->createSurface(this)) { connect(qApp, &QGuiApplication::screenRemoved, this, &QWaylandSurface::handleScreenRemoved); + connect(qApp, &QGuiApplication::screenAdded, this, &QWaylandSurface::screensChanged); } QWaylandSurface::~QWaylandSurface() @@ -60,7 +25,14 @@ QWaylandSurface::~QWaylandSurface() QWaylandScreen *QWaylandSurface::oldestEnteredScreen() { - return m_screens.value(0, nullptr); + for (auto *screen : std::as_const(m_screens)) { + // only report valid screens + // we can have some ouptuts waiting for xdg output information + // that are valid QPlatformScreens, but not valid QScreens + if (screen->screen()) + return screen; + } + return nullptr; } QWaylandSurface *QWaylandSurface::fromWlSurface(::wl_surface *surface) @@ -72,8 +44,12 @@ QWaylandSurface *QWaylandSurface::fromWlSurface(::wl_surface *surface) void QWaylandSurface::handleScreenRemoved(QScreen *qScreen) { - auto *screen = static_cast<QWaylandScreen *>(qScreen->handle()); - if (m_screens.removeOne(screen)) + auto *platformScreen = qScreen->handle(); + if (platformScreen->isPlaceholder()) + return; + + auto *waylandScreen = static_cast<QWaylandScreen *>(qScreen->handle()); + if (m_screens.removeOne(waylandScreen)) emit screensChanged(); } @@ -117,6 +93,24 @@ void QWaylandSurface::surface_leave(wl_output *output) emit screensChanged(); } +void QWaylandSurface::surface_preferred_buffer_scale(int32_t scale) +{ + if (m_preferredBufferScale == scale) + return; + m_preferredBufferScale = scale; + Q_EMIT preferredBufferScaleChanged(); +} + +void QWaylandSurface::surface_preferred_buffer_transform(uint32_t transform) +{ + if (m_preferredBufferTransform == transform) + return; + m_preferredBufferTransform = static_cast<wl_output_transform>(transform); + Q_EMIT preferredBufferTransformChanged(); +} + } // namespace QtWaylandClient QT_END_NAMESPACE + +#include "moc_qwaylandsurface_p.cpp" diff --git a/src/client/qwaylandsurface_p.h b/src/client/qwaylandsurface_p.h index 541010934..41860297e 100644 --- a/src/client/qwaylandsurface_p.h +++ b/src/client/qwaylandsurface_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the config.tests of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDSURFACE_P_H #define QWAYLANDSURFACE_P_H @@ -54,6 +18,7 @@ #include <QtGui/QScreen> #include <QtWaylandClient/private/qwayland-wayland.h> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -71,21 +36,29 @@ public: ~QWaylandSurface() override; QWaylandScreen *oldestEnteredScreen(); QWaylandWindow *waylandWindow() const { return m_window; } + std::optional<int32_t> preferredBufferScale() const { return m_preferredBufferScale; } + std::optional<wl_output_transform> preferredBufferTransform() const { return m_preferredBufferTransform; } static QWaylandSurface *fromWlSurface(::wl_surface *surface); -signals: +Q_SIGNALS: void screensChanged(); + void preferredBufferScaleChanged(); + void preferredBufferTransformChanged(); -private slots: +private Q_SLOTS: void handleScreenRemoved(QScreen *qScreen); protected: void surface_enter(struct ::wl_output *output) override; void surface_leave(struct ::wl_output *output) override; + void surface_preferred_buffer_scale(int32_t scale) override; + void surface_preferred_buffer_transform(uint32_t transform) override; - QVector<QWaylandScreen *> m_screens; //As seen by wl_surface.enter/leave events. Chronological order. + QList<QWaylandScreen *> m_screens; //As seen by wl_surface.enter/leave events. Chronological order. QWaylandWindow *m_window = nullptr; + std::optional<int32_t> m_preferredBufferScale; + std::optional<wl_output_transform> m_preferredBufferTransform; friend class QWaylandWindow; // TODO: shouldn't need to be friends }; diff --git a/src/client/qwaylandtabletv2.cpp b/src/client/qwaylandtabletv2.cpp new file mode 100644 index 000000000..73524c166 --- /dev/null +++ b/src/client/qwaylandtabletv2.cpp @@ -0,0 +1,314 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwaylandtabletv2_p.h" +#include "qwaylandinputdevice_p.h" +#include "qwaylanddisplay_p.h" +#include "qwaylandsurface_p.h" + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +QWaylandTabletManagerV2::QWaylandTabletManagerV2(QWaylandDisplay *display, uint id, uint version) + : zwp_tablet_manager_v2(display->wl_registry(), id, qMin(version, uint(1))) +{ + // Create tabletSeats for all seats. + // This only works if we get the manager after all seats + const auto seats = display->inputDevices(); + for (auto *seat : seats) + createTabletSeat(seat); +} + +QWaylandTabletManagerV2::~QWaylandTabletManagerV2() +{ + destroy(); +} + +QWaylandTabletSeatV2 *QWaylandTabletManagerV2::createTabletSeat(QWaylandInputDevice *seat) +{ + return new QWaylandTabletSeatV2(this, seat); +} + +QWaylandTabletSeatV2::QWaylandTabletSeatV2(QWaylandTabletManagerV2 *manager, QWaylandInputDevice *seat) + : QtWayland::zwp_tablet_seat_v2(manager->get_tablet_seat(seat->wl_seat())) + , m_seat(seat) +{ +} + +QWaylandTabletSeatV2::~QWaylandTabletSeatV2() +{ + for (auto *tablet : m_tablets) + tablet->destroy(); + for (auto *tool : m_tools) + tool->destroy(); + for (auto *pad : m_pads) + pad->destroy(); + qDeleteAll(m_tablets); + qDeleteAll(m_tools); + qDeleteAll(m_pads); + destroy(); +} + +void QWaylandTabletSeatV2::zwp_tablet_seat_v2_tablet_added(zwp_tablet_v2 *id) +{ + auto *tablet = new QWaylandTabletV2(id); + m_tablets.push_back(tablet); + connect(tablet, &QWaylandTabletV2::destroyed, this, [this, tablet] { m_tablets.removeOne(tablet); }); +} + +void QWaylandTabletSeatV2::zwp_tablet_seat_v2_tool_added(zwp_tablet_tool_v2 *id) +{ + auto *tool = new QWaylandTabletToolV2(this, id); + m_tools.push_back(tool); + connect(tool, &QWaylandTabletToolV2::destroyed, this, [this, tool] { m_tools.removeOne(tool); }); +} + +void QWaylandTabletSeatV2::zwp_tablet_seat_v2_pad_added(zwp_tablet_pad_v2 *id) +{ + auto *pad = new QWaylandTabletPadV2(id); + m_pads.push_back(pad); + connect(pad, &QWaylandTabletPadV2::destroyed, this, [this, pad] { m_pads.removeOne(pad); }); +} + +QWaylandTabletV2::QWaylandTabletV2(::zwp_tablet_v2 *tablet) + : QtWayland::zwp_tablet_v2(tablet) +{ +} + +void QWaylandTabletV2::zwp_tablet_v2_removed() +{ + destroy(); + delete this; +} + +QWaylandTabletToolV2::QWaylandTabletToolV2(QWaylandTabletSeatV2 *tabletSeat, ::zwp_tablet_tool_v2 *tool) + : QtWayland::zwp_tablet_tool_v2(tool) + , m_tabletSeat(tabletSeat) +{ +} + +void QWaylandTabletToolV2::zwp_tablet_tool_v2_type(uint32_t tool_type) +{ + m_toolType = type(tool_type); +} + +void QWaylandTabletToolV2::zwp_tablet_tool_v2_hardware_serial(uint32_t hardware_serial_hi, uint32_t hardware_serial_lo) +{ + m_uid = (quint64(hardware_serial_hi) << 32) + hardware_serial_lo; +} + +void QWaylandTabletToolV2::zwp_tablet_tool_v2_capability(uint32_t capability) +{ + if (capability == capability_rotation) + m_hasRotation = true; +} + +void QWaylandTabletToolV2::zwp_tablet_tool_v2_done() +{ + switch (m_toolType) { + case type::type_airbrush: + case type::type_brush: + case type::type_pencil: + case type::type_pen: + m_pointerType = QPointingDevice::PointerType::Pen; + break; + case type::type_eraser: + m_pointerType = QPointingDevice::PointerType::Eraser; + break; + case type::type_mouse: + case type::type_lens: + m_pointerType = QPointingDevice::PointerType::Cursor; + break; + case type::type_finger: + m_pointerType = QPointingDevice::PointerType::Unknown; + break; + } + switch (m_toolType) { + case type::type_airbrush: + m_tabletDevice = QInputDevice::DeviceType::Airbrush; + break; + case type::type_brush: + case type::type_pencil: + case type::type_pen: + case type::type_eraser: + m_tabletDevice = QInputDevice::DeviceType::Stylus; + break; + case type::type_lens: + m_tabletDevice = QInputDevice::DeviceType::Puck; + break; + case type::type_mouse: + case type::type_finger: + m_tabletDevice = QInputDevice::DeviceType::Unknown; + break; + } +} + +void QWaylandTabletToolV2::zwp_tablet_tool_v2_removed() +{ + destroy(); + delete this; +} + +void QWaylandTabletToolV2::zwp_tablet_tool_v2_proximity_in(uint32_t serial, zwp_tablet_v2 *tablet, wl_surface *surface) +{ + Q_UNUSED(tablet); + Q_UNUSED(serial); + if (Q_UNLIKELY(!surface)) { + qCDebug(lcQpaWayland) << "Ignoring zwp_tablet_tool_v2_proximity_v2 with no surface"; + return; + } + m_pending.enteredSurface = true; + m_pending.proximitySurface = QWaylandSurface::fromWlSurface(surface); +} + +void QWaylandTabletToolV2::zwp_tablet_tool_v2_proximity_out() +{ + m_pending.enteredSurface = false; + m_pending.proximitySurface = nullptr; +} + +void QWaylandTabletToolV2::zwp_tablet_tool_v2_down(uint32_t serial) +{ + m_pending.down = true; + + if (m_pending.proximitySurface) { + if (QWaylandWindow *window = m_pending.proximitySurface->waylandWindow()) { + QWaylandInputDevice *seat = m_tabletSeat->seat(); + seat->display()->setLastInputDevice(seat, serial, window); + } + } +} + +void QWaylandTabletToolV2::zwp_tablet_tool_v2_up() +{ + m_pending.down = false; +} + +void QWaylandTabletToolV2::zwp_tablet_tool_v2_motion(wl_fixed_t x, wl_fixed_t y) +{ + m_pending.surfacePosition = QPointF(wl_fixed_to_double(x), wl_fixed_to_double(y)); +} + +void QWaylandTabletToolV2::zwp_tablet_tool_v2_pressure(uint32_t pressure) +{ + const int maxPressure = 65535; + m_pending.pressure = qreal(pressure)/maxPressure; +} + +void QWaylandTabletToolV2::zwp_tablet_tool_v2_distance(uint32_t distance) +{ + m_pending.distance = distance; +} + +void QWaylandTabletToolV2::zwp_tablet_tool_v2_tilt(wl_fixed_t tilt_x, wl_fixed_t tilt_y) +{ + m_pending.xTilt = wl_fixed_to_double(tilt_x); + m_pending.yTilt = wl_fixed_to_double(tilt_y); +} + +void QWaylandTabletToolV2::zwp_tablet_tool_v2_rotation(wl_fixed_t degrees) +{ + m_pending.rotation = wl_fixed_to_double(degrees); +} + +void QWaylandTabletToolV2::zwp_tablet_tool_v2_slider(int32_t position) +{ + m_pending.slider = qreal(position) / 65535; +} + +static Qt::MouseButton mouseButtonFromTablet(uint button) +{ + switch (button) { + case 0x110: return Qt::MouseButton::LeftButton; // BTN_LEFT + case 0x14b: return Qt::MouseButton::MiddleButton; // BTN_STYLUS + case 0x14c: return Qt::MouseButton::RightButton; // BTN_STYLUS2 + default: + return Qt::NoButton; + } +} + +void QWaylandTabletToolV2::zwp_tablet_tool_v2_button(uint32_t serial, uint32_t button, uint32_t state) +{ + Q_UNUSED(serial); + Qt::MouseButton mouseButton = mouseButtonFromTablet(button); + if (state == button_state_pressed) + m_pending.buttons |= mouseButton; + else + m_pending.buttons &= ~mouseButton; +} + +void QWaylandTabletToolV2::zwp_tablet_tool_v2_frame(uint32_t time) +{ + if (m_pending.proximitySurface && !m_applied.proximitySurface) { + QWindowSystemInterface::handleTabletEnterProximityEvent(int(m_tabletDevice), int(m_pointerType), m_uid); + m_applied.proximitySurface = m_pending.proximitySurface; + } + + if (!(m_pending == m_applied) && m_pending.proximitySurface) { + if (!m_pending.proximitySurface) { + qCWarning(lcQpaWayland) << "Can't send tablet event with no proximity surface, ignoring"; + return; + } + QWaylandWindow *waylandWindow = QWaylandWindow::fromWlSurface(m_pending.proximitySurface->object()); + QWindow *window = waylandWindow->window(); + ulong timestamp = time; + const QPointF localPosition = waylandWindow->mapFromWlSurface(m_pending.surfacePosition); + + QPointF delta = localPosition - localPosition.toPoint(); + QPointF globalPosition = window->mapToGlobal(localPosition.toPoint()); + globalPosition += delta; + + Qt::MouseButtons buttons = m_pending.down ? Qt::MouseButton::LeftButton : Qt::MouseButton::NoButton; + buttons |= m_pending.buttons; + qreal pressure = m_pending.pressure; + int xTilt = int(m_pending.xTilt); + int yTilt = int(m_pending.yTilt); + qreal tangentialPressure = m_pending.slider; + qreal rotation = m_pending.rotation; + int z = int(m_pending.distance); + QWindowSystemInterface::handleTabletEvent(window, timestamp, localPosition, globalPosition, + int(m_tabletDevice), int(m_pointerType), buttons, pressure, + xTilt, yTilt, tangentialPressure, rotation, z, m_uid); + } + + if (!m_pending.proximitySurface && m_applied.enteredSurface) { + QWindowSystemInterface::handleTabletLeaveProximityEvent(int(m_tabletDevice), int(m_pointerType), m_uid); + m_pending = State(); // Don't leave pressure etc. lying around when we enter the next surface + } + + m_applied = m_pending; +} + +// TODO: delete when upgrading to c++20 +bool QWaylandTabletToolV2::State::operator==(const QWaylandTabletToolV2::State &o) const { + return + down == o.down && + proximitySurface.data() == o.proximitySurface.data() && + enteredSurface == o.enteredSurface && + surfacePosition == o.surfacePosition && + distance == o.distance && + pressure == o.pressure && + rotation == o.rotation && + xTilt == o.xTilt && + yTilt == o.yTilt && + slider == o.slider && + buttons == o.buttons; +} + +QWaylandTabletPadV2::QWaylandTabletPadV2(::zwp_tablet_pad_v2 *pad) + : QtWayland::zwp_tablet_pad_v2(pad) +{ +} + +void QWaylandTabletPadV2::zwp_tablet_pad_v2_removed() +{ + destroy(); + delete this; +} + +} // namespace QtWaylandClient + +QT_END_NAMESPACE + +#include "moc_qwaylandtabletv2_p.cpp" diff --git a/src/client/qwaylandtabletv2_p.h b/src/client/qwaylandtabletv2_p.h new file mode 100644 index 000000000..20a8a4f5a --- /dev/null +++ b/src/client/qwaylandtabletv2_p.h @@ -0,0 +1,161 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWAYLANDTABLETV2_P_H +#define QWAYLANDTABLETV2_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtWaylandClient/private/qwayland-tablet-unstable-v2.h> + +#include <QtWaylandClient/private/qtwaylandclientglobal_p.h> + +#include <QtCore/QObject> +#include <QtCore/QPointer> +#include <QtCore/QPointF> +#include <QtGui/QPointingDevice> +#include <QtGui/QInputDevice> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class QWaylandDisplay; +class QWaylandInputDevice; +class QWaylandSurface; + +class QWaylandTabletSeatV2; +class QWaylandTabletV2; +class QWaylandTabletToolV2; +class QWaylandTabletPadV2; + +class Q_WAYLANDCLIENT_EXPORT QWaylandTabletManagerV2 : public QtWayland::zwp_tablet_manager_v2 +{ +public: + explicit QWaylandTabletManagerV2(QWaylandDisplay *display, uint id, uint version); + ~QWaylandTabletManagerV2() override; + QWaylandTabletSeatV2 *createTabletSeat(QWaylandInputDevice *seat); +}; + +class Q_WAYLANDCLIENT_EXPORT QWaylandTabletSeatV2 : public QObject, public QtWayland::zwp_tablet_seat_v2 +{ + Q_OBJECT +public: + explicit QWaylandTabletSeatV2(QWaylandTabletManagerV2 *manager, QWaylandInputDevice *seat); + ~QWaylandTabletSeatV2() override; + + QWaylandInputDevice *seat() const { return m_seat; } + +protected: + void zwp_tablet_seat_v2_tablet_added(struct ::zwp_tablet_v2 *id) override; + void zwp_tablet_seat_v2_tool_added(struct ::zwp_tablet_tool_v2 *id) override; + void zwp_tablet_seat_v2_pad_added(struct ::zwp_tablet_pad_v2 *id) override; + +private: + QWaylandInputDevice *m_seat; + QList<QWaylandTabletV2 *> m_tablets; + QList<QWaylandTabletToolV2 *> m_tools; + QList<QWaylandTabletPadV2 *> m_pads; +}; + +class Q_WAYLANDCLIENT_EXPORT QWaylandTabletV2 : public QObject, public QtWayland::zwp_tablet_v2 +{ + Q_OBJECT +public: + explicit QWaylandTabletV2(::zwp_tablet_v2 *tablet); + +protected: +// void zwp_tablet_v2_name(const QString &name) override; +// void zwp_tablet_v2_id(uint32_t vid, uint32_t pid) override; +// void zwp_tablet_v2_path(const QString &path) override; +// void zwp_tablet_v2_done() override; + void zwp_tablet_v2_removed() override; +}; + +class Q_WAYLANDCLIENT_EXPORT QWaylandTabletToolV2 : public QObject, public QtWayland::zwp_tablet_tool_v2 +{ + Q_OBJECT +public: + QWaylandTabletToolV2(QWaylandTabletSeatV2 *tabletSeat, ::zwp_tablet_tool_v2 *tool); + +protected: + void zwp_tablet_tool_v2_type(uint32_t tool_type) override; + void zwp_tablet_tool_v2_hardware_serial(uint32_t hardware_serial_hi, uint32_t hardware_serial_lo) override; +// void zwp_tablet_tool_v2_hardware_id_wacom(uint32_t hardware_id_hi, uint32_t hardware_id_lo) override; + void zwp_tablet_tool_v2_capability(uint32_t capability) override; + void zwp_tablet_tool_v2_done() override; + void zwp_tablet_tool_v2_removed() override; + void zwp_tablet_tool_v2_proximity_in(uint32_t serial, struct ::zwp_tablet_v2 *tablet, struct ::wl_surface *surface) override; + void zwp_tablet_tool_v2_proximity_out() override; + void zwp_tablet_tool_v2_down(uint32_t serial) override; + void zwp_tablet_tool_v2_up() override; + void zwp_tablet_tool_v2_motion(wl_fixed_t x, wl_fixed_t y) override; + void zwp_tablet_tool_v2_pressure(uint32_t pressure) override; + void zwp_tablet_tool_v2_distance(uint32_t distance) override; + void zwp_tablet_tool_v2_tilt(wl_fixed_t tilt_x, wl_fixed_t tilt_y) override; + void zwp_tablet_tool_v2_rotation(wl_fixed_t degrees) override; + void zwp_tablet_tool_v2_slider(int32_t position) override; +// void zwp_tablet_tool_v2_wheel(wl_fixed_t degrees, int32_t clicks) override; + void zwp_tablet_tool_v2_button(uint32_t serial, uint32_t button, uint32_t state) override; + void zwp_tablet_tool_v2_frame(uint32_t time) override; + +private: + QWaylandTabletSeatV2 *m_tabletSeat; + + // Static state (sent before done event) + QPointingDevice::PointerType m_pointerType = QPointingDevice::PointerType::Unknown; + QInputDevice::DeviceType m_tabletDevice = QInputDevice::DeviceType::Unknown; + type m_toolType = type_pen; + bool m_hasRotation = false; + quint64 m_uid = 0; + + // Accumulated state (applied on frame event) + struct State { + bool down = false; + QPointer<QWaylandSurface> proximitySurface; + bool enteredSurface = false; // Not enough with just proximitySurface, if the surface is deleted, we still want to send a leave event + QPointF surfacePosition; + uint distance = 0; + qreal pressure = 0; + qreal rotation = 0; + qreal xTilt = 0; + qreal yTilt = 0; + qreal slider = 0; + Qt::MouseButtons buttons = Qt::MouseButton::NoButton; // Actual buttons, down state -> left mouse is mapped inside the frame handler + //auto operator<=>(const Point&) const = default; // TODO: use this when upgrading to C++20 + bool operator==(const State &o) const; + } m_pending, m_applied; +}; + +// We don't actually use this, but need to handle the "removed" event to comply with the protocol +class Q_WAYLANDCLIENT_EXPORT QWaylandTabletPadV2 : public QObject, public QtWayland::zwp_tablet_pad_v2 +{ + Q_OBJECT +public: + explicit QWaylandTabletPadV2(::zwp_tablet_pad_v2 *pad); + +protected: +// void zwp_tablet_pad_v2_group(struct ::zwp_tablet_pad_group_v2 *pad_group) override; +// void zwp_tablet_pad_v2_path(const QString &path) override; +// void zwp_tablet_pad_v2_buttons(uint32_t buttons) override; +// void zwp_tablet_pad_v2_done() override; +// void zwp_tablet_pad_v2_button(uint32_t time, uint32_t button, uint32_t state) override; +// void zwp_tablet_pad_v2_enter(uint32_t serial, struct ::zwp_tablet_v2 *tablet, struct ::wl_surface *surface) override; +// void zwp_tablet_pad_v2_leave(uint32_t serial, struct ::wl_surface *surface) override; + void zwp_tablet_pad_v2_removed() override; +}; + +} // namespace QtWaylandClient + +QT_END_NAMESPACE + +#endif // QWAYLANDTABLETV2_P_H diff --git a/src/client/qwaylandtextinputinterface.cpp b/src/client/qwaylandtextinputinterface.cpp new file mode 100644 index 000000000..794396475 --- /dev/null +++ b/src/client/qwaylandtextinputinterface.cpp @@ -0,0 +1,8 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwaylandtextinputinterface_p.h" + +QT_BEGIN_NAMESPACE + +QT_END_NAMESPACE diff --git a/src/client/qwaylandtextinputinterface_p.h b/src/client/qwaylandtextinputinterface_p.h new file mode 100644 index 000000000..6f695575f --- /dev/null +++ b/src/client/qwaylandtextinputinterface_p.h @@ -0,0 +1,61 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWAYLANDTEXTINPUTINTERFACE_P_H +#define QWAYLANDTEXTINPUTINTERFACE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qlocale.h> +#include <QtCore/qrect.h> +#include <QtCore/private/qglobal_p.h> + +struct wl_surface; + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class QWaylandTextInputInterface +{ +public: + virtual ~QWaylandTextInputInterface() {} + virtual void reset() = 0; + virtual void commit() = 0; + virtual void disableSurface(::wl_surface *surface) = 0; + virtual void enableSurface(::wl_surface *surface) = 0; + virtual void updateState(Qt::InputMethodQueries queries, uint32_t flags) = 0; + virtual void showInputPanel() {} + virtual void hideInputPanel() {} + virtual bool isInputPanelVisible() const = 0; + virtual QRectF keyboardRect() const = 0; + virtual QLocale locale() const = 0; + virtual Qt::LayoutDirection inputDirection() const = 0; + virtual void setCursorInsidePreedit(int cursor) = 0; + + // This enum should be compatible with update_state of text-input-unstable-v2. + // Higher versions of text-input-* protocol may not use it directly + // but QtWaylandClient can determine clients' states based on the values + enum TextInputState { + update_state_change = 0, // updated state because it changed + update_state_full = 1, // full state after enter or input_method_changed event + update_state_reset = 2, // full state after reset + update_state_enter = 3, // full state after switching focus to a different widget on client side + }; +}; + +} + +QT_END_NAMESPACE + +#endif // QWAYLANDTEXTINPUTINTERFACE_P_H + diff --git a/src/client/qwaylandtextinputv1.cpp b/src/client/qwaylandtextinputv1.cpp new file mode 100644 index 000000000..846ed95c8 --- /dev/null +++ b/src/client/qwaylandtextinputv1.cpp @@ -0,0 +1,362 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#include <qpa/qplatforminputcontext.h> + +#include "qwaylandtextinputv1_p.h" + +#include "qwaylandwindow_p.h" +#include "qwaylandinputmethodeventbuilder_p.h" + +#include <QtCore/qloggingcategory.h> +#include <QtGui/QGuiApplication> +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/private/qhighdpiscaling_p.h> +#include <QtGui/qpa/qplatformintegration.h> +#include <QtGui/qevent.h> +#include <QtGui/qwindow.h> +#include <QTextCharFormat> +#include <QList> +#include <QRectF> +#include <QLocale> + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(qLcQpaInputMethods) + +namespace QtWaylandClient { + +namespace { + +const Qt::InputMethodQueries supportedQueries1 = Qt::ImEnabled | + Qt::ImSurroundingText | + Qt::ImCursorPosition | + Qt::ImAnchorPosition | + Qt::ImHints | + Qt::ImCursorRectangle | + Qt::ImPreferredLanguage; +} + +QWaylandTextInputv1::QWaylandTextInputv1(QWaylandDisplay *display, struct ::zwp_text_input_v1 *text_input) + : QtWayland::zwp_text_input_v1(text_input) +{ + Q_UNUSED(display); +} + +QWaylandTextInputv1::~QWaylandTextInputv1() +{ + if (m_resetCallback) + wl_callback_destroy(m_resetCallback); + zwp_text_input_v1_destroy(object()); +} + +void QWaylandTextInputv1::reset() +{ + m_builder.reset(); + m_preeditCommit = QString(); + updateState(Qt::ImQueryAll, QWaylandTextInputInterface::update_state_reset); +} + +void QWaylandTextInputv1::commit() +{ + if (QObject *o = QGuiApplication::focusObject()) { + QInputMethodEvent event; + event.setCommitString(m_preeditCommit); + QCoreApplication::sendEvent(o, &event); + } + + reset(); +} + +const wl_callback_listener QWaylandTextInputv1::callbackListener = { + QWaylandTextInputv1::resetCallback +}; + +void QWaylandTextInputv1::resetCallback(void *data, wl_callback *, uint32_t) +{ + QWaylandTextInputv1 *self = static_cast<QWaylandTextInputv1*>(data); + + if (self->m_resetCallback) { + wl_callback_destroy(self->m_resetCallback); + self->m_resetCallback = nullptr; + } +} + +void QWaylandTextInputv1::updateState(Qt::InputMethodQueries queries, uint32_t flags) +{ + if (!QGuiApplication::focusObject()) + return; + + if (!QGuiApplication::focusWindow() || !QGuiApplication::focusWindow()->handle()) + return; + + auto *window = static_cast<QWaylandWindow *>(QGuiApplication::focusWindow()->handle()); + auto *surface = window->wlSurface(); + if (!surface || (surface != m_surface)) + return; + + queries &= supportedQueries1; + + // Surrounding text, cursor and anchor positions are transferred together + if ((queries & Qt::ImSurroundingText) || (queries & Qt::ImCursorPosition) || (queries & Qt::ImAnchorPosition)) + queries |= Qt::ImSurroundingText | Qt::ImCursorPosition | Qt::ImAnchorPosition; + + QInputMethodQueryEvent event(queries); + QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event); + + if ((queries & Qt::ImSurroundingText) || (queries & Qt::ImCursorPosition) || (queries & Qt::ImAnchorPosition)) { + QString text = event.value(Qt::ImSurroundingText).toString(); + int cursor = event.value(Qt::ImCursorPosition).toInt(); + int anchor = event.value(Qt::ImAnchorPosition).toInt(); + + // Make sure text is not too big + if (text.toUtf8().size() > 2048) { + int c = qAbs(cursor - anchor) <= 512 ? qMin(cursor, anchor) + qAbs(cursor - anchor) / 2: cursor; + + const int offset = c - qBound(0, c, 512 - qMin(text.size() - c, 256)); + text = text.mid(offset + c - 256, 512); + cursor -= offset; + anchor -= offset; + } + + set_surrounding_text(text, QWaylandInputMethodEventBuilder::indexToWayland(text, cursor), QWaylandInputMethodEventBuilder::indexToWayland(text, anchor)); + } + + if (queries & Qt::ImHints) { + QWaylandInputMethodContentType contentType = QWaylandInputMethodContentType::convert(static_cast<Qt::InputMethodHints>(event.value(Qt::ImHints).toInt())); + set_content_type(contentType.hint, contentType.purpose); + } + + if (queries & Qt::ImCursorRectangle) { + const QRect &cRect = event.value(Qt::ImCursorRectangle).toRect(); + const QRect &windowRect = QGuiApplication::inputMethod()->inputItemTransform().mapRect(cRect); + const QRect &nativeRect = QHighDpi::toNativePixels(windowRect, QGuiApplication::focusWindow()); + const QMargins margins = window->clientSideMargins(); + const QRect &surfaceRect = nativeRect.translated(margins.left(), margins.top()); + set_cursor_rectangle(surfaceRect.x(), surfaceRect.y(), surfaceRect.width(), surfaceRect.height()); + } + + if (queries & Qt::ImPreferredLanguage) { + const QString &language = event.value(Qt::ImPreferredLanguage).toString(); + set_preferred_language(language); + } + + if (flags == QWaylandTextInputInterface::update_state_reset) + QtWayland::zwp_text_input_v1::reset(); + else + commit_state(m_serial); +} + +void QWaylandTextInputv1::setCursorInsidePreedit(int) +{ + // Not supported yet +} + +bool QWaylandTextInputv1::isInputPanelVisible() const +{ + return m_inputPanelVisible; +} + +QRectF QWaylandTextInputv1::keyboardRect() const +{ + return m_keyboardRectangle; +} + +QLocale QWaylandTextInputv1::locale() const +{ + return m_locale; +} + +Qt::LayoutDirection QWaylandTextInputv1::inputDirection() const +{ + return m_inputDirection; +} + +void QWaylandTextInputv1::zwp_text_input_v1_enter(::wl_surface *surface) +{ + m_surface = surface; + + updateState(Qt::ImQueryAll, QWaylandTextInputInterface::update_state_reset); +} + +void QWaylandTextInputv1::zwp_text_input_v1_leave() +{ + m_surface = nullptr; +} + +void QWaylandTextInputv1::zwp_text_input_v1_modifiers_map(wl_array *map) +{ + const QList<QByteArray> modifiersMap = QByteArray::fromRawData(static_cast<const char*>(map->data), map->size).split('\0'); + + m_modifiersMap.clear(); + + for (const QByteArray &modifier : modifiersMap) { + if (modifier == "Shift") + m_modifiersMap.append(Qt::ShiftModifier); + else if (modifier == "Control") + m_modifiersMap.append(Qt::ControlModifier); + else if (modifier == "Alt") + m_modifiersMap.append(Qt::AltModifier); + else if (modifier == "Mod1") + m_modifiersMap.append(Qt::AltModifier); + else if (modifier == "Mod4") + m_modifiersMap.append(Qt::MetaModifier); + else + m_modifiersMap.append(Qt::NoModifier); + } +} + +void QWaylandTextInputv1::zwp_text_input_v1_input_panel_state(uint32_t visible) +{ + const bool inputPanelVisible = (visible == 1); + if (m_inputPanelVisible != inputPanelVisible) { + m_inputPanelVisible = inputPanelVisible; + QGuiApplicationPrivate::platformIntegration()->inputContext()->emitInputPanelVisibleChanged(); + } +} + +void QWaylandTextInputv1::zwp_text_input_v1_preedit_string(uint32_t serial, const QString &text, const QString &commit) +{ + m_serial = serial; + + if (m_resetCallback) { + qCDebug(qLcQpaInputMethods()) << "discard preedit_string: reset not confirmed"; + m_builder.reset(); + return; + } + + if (!QGuiApplication::focusObject()) + return; + + QInputMethodEvent *event = m_builder.buildPreedit(text); + + m_builder.reset(); + m_preeditCommit = commit; + + QCoreApplication::sendEvent(QGuiApplication::focusObject(), event); + delete event; +} + +void QWaylandTextInputv1::zwp_text_input_v1_preedit_styling(uint32_t index, uint32_t length, uint32_t style) +{ + m_builder.addPreeditStyling(index, length, style); +} + +void QWaylandTextInputv1::zwp_text_input_v1_preedit_cursor(int32_t index) +{ + m_builder.setPreeditCursor(index); +} + +void QWaylandTextInputv1::zwp_text_input_v1_commit_string(uint32_t serial, const QString &text) +{ + m_serial = serial; + + if (m_resetCallback) { + qCDebug(qLcQpaInputMethods()) << "discard commit_string: reset not confirmed"; + m_builder.reset(); + return; + } + + if (!QGuiApplication::focusObject()) + return; + + // When committing the text, the preeditString needs to be reset, to prevent it to be + // send again in the commit() function + m_preeditCommit.clear(); + + QInputMethodEvent *event = m_builder.buildCommit(text); + + m_builder.reset(); + + QCoreApplication::sendEvent(QGuiApplication::focusObject(), event); + delete event; +} + +void QWaylandTextInputv1::zwp_text_input_v1_cursor_position(int32_t index, int32_t anchor) +{ + m_builder.setCursorPosition(index, anchor); +} + +void QWaylandTextInputv1::zwp_text_input_v1_delete_surrounding_text(int32_t before_length, uint32_t after_length) +{ + //before_length is negative, but the builder expects it to be positive + m_builder.setDeleteSurroundingText(-before_length, after_length); +} + +void QWaylandTextInputv1::zwp_text_input_v1_keysym(uint32_t serial, uint32_t time, uint32_t sym, uint32_t state, uint32_t modifiers) +{ + m_serial = serial; + +#if QT_CONFIG(xkbcommon) + if (m_resetCallback) { + qCDebug(qLcQpaInputMethods()) << "discard keysym: reset not confirmed"; + return; + } + + if (!QGuiApplication::focusWindow()) + return; + + Qt::KeyboardModifiers qtModifiers = modifiersToQtModifiers(modifiers); + + QEvent::Type type = state == WL_KEYBOARD_KEY_STATE_PRESSED ? QEvent::KeyPress : QEvent::KeyRelease; + QString text = QXkbCommon::lookupStringNoKeysymTransformations(sym); + int qtkey = QXkbCommon::keysymToQtKey(sym, qtModifiers); + + QWindowSystemInterface::handleKeyEvent(QGuiApplication::focusWindow(), + time, type, qtkey, qtModifiers, text); +#else + Q_UNUSED(time); + Q_UNUSED(sym); + Q_UNUSED(state); + Q_UNUSED(modifiers); +#endif +} + +void QWaylandTextInputv1::zwp_text_input_v1_language(uint32_t serial, const QString &language) +{ + m_serial = serial; + + if (m_resetCallback) { + qCDebug(qLcQpaInputMethods()) << "discard language: reset not confirmed"; + return; + } + + const QLocale locale(language); + if (m_locale != locale) { + m_locale = locale; + QGuiApplicationPrivate::platformIntegration()->inputContext()->emitLocaleChanged(); + } +} + +void QWaylandTextInputv1::zwp_text_input_v1_text_direction(uint32_t serial, uint32_t direction) +{ + m_serial = serial; + + if (m_resetCallback) { + qCDebug(qLcQpaInputMethods()) << "discard text_direction: reset not confirmed"; + return; + } + + const Qt::LayoutDirection inputDirection = (direction == text_direction_auto) ? Qt::LayoutDirectionAuto : + (direction == text_direction_ltr) ? Qt::LeftToRight : + (direction == text_direction_rtl) ? Qt::RightToLeft : Qt::LayoutDirectionAuto; + if (m_inputDirection != inputDirection) { + m_inputDirection = inputDirection; + QGuiApplicationPrivate::platformIntegration()->inputContext()->emitInputDirectionChanged(m_inputDirection); + } +} + +Qt::KeyboardModifiers QWaylandTextInputv1::modifiersToQtModifiers(uint32_t modifiers) +{ + Qt::KeyboardModifiers ret = Qt::NoModifier; + for (int i = 0; i < m_modifiersMap.size(); ++i) { + if (modifiers & (1 << i)) { + ret |= m_modifiersMap[i]; + } + } + return ret; +} + +} + +QT_END_NAMESPACE + diff --git a/src/client/qwaylandtextinputv1_p.h b/src/client/qwaylandtextinputv1_p.h new file mode 100644 index 000000000..dc591cdb1 --- /dev/null +++ b/src/client/qwaylandtextinputv1_p.h @@ -0,0 +1,112 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + + +#ifndef QWAYLANDTEXTINPUTV1_H +#define QWAYLANDTEXTINPUTV1_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qwaylandtextinputinterface_p.h" +#include <QtWaylandClient/private/qwayland-text-input-unstable-v1.h> +#include <qwaylandinputmethodeventbuilder_p.h> + +struct wl_callback; +struct wl_callback_listener; + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class QWaylandDisplay; + +class QWaylandTextInputv1 : public QtWayland::zwp_text_input_v1, public QWaylandTextInputInterface +{ +public: + QWaylandTextInputv1(QWaylandDisplay *display, struct ::zwp_text_input_v1 *text_input); + ~QWaylandTextInputv1() override; + + void setSeat(struct ::wl_seat *seat) { m_seat = seat; } + + void reset() override; + void commit() override; + void updateState(Qt::InputMethodQueries queries, uint32_t flags) override; + + void setCursorInsidePreedit(int cursor) override; + + bool isInputPanelVisible() const override; + QRectF keyboardRect() const override; + + QLocale locale() const override; + Qt::LayoutDirection inputDirection() const override; + + void showInputPanel() override + { + show_input_panel(); + } + void hideInputPanel() override + { + hide_input_panel(); + } + void enableSurface(::wl_surface *surface) override + { + activate(m_seat, surface); + } + void disableSurface(::wl_surface *surface) override + { + Q_UNUSED(surface); + deactivate(m_seat); + } + +protected: + void zwp_text_input_v1_enter(struct ::wl_surface *surface) override; + void zwp_text_input_v1_leave() override; + void zwp_text_input_v1_modifiers_map(wl_array *map) override; + void zwp_text_input_v1_input_panel_state(uint32_t state) override; + void zwp_text_input_v1_preedit_string(uint32_t serial, const QString &text, const QString &commit) override; + void zwp_text_input_v1_preedit_styling(uint32_t index, uint32_t length, uint32_t style) override; + void zwp_text_input_v1_preedit_cursor(int32_t index) override; + void zwp_text_input_v1_commit_string(uint32_t serial, const QString &text) override; + void zwp_text_input_v1_cursor_position(int32_t index, int32_t anchor) override; + void zwp_text_input_v1_delete_surrounding_text(int32_t before_length, uint32_t after_length) override; + void zwp_text_input_v1_keysym(uint32_t serial, uint32_t time, uint32_t sym, uint32_t state, uint32_t modifiers) override; + void zwp_text_input_v1_language(uint32_t serial, const QString &language) override; + void zwp_text_input_v1_text_direction(uint32_t serial, uint32_t direction) override; + +private: + Qt::KeyboardModifiers modifiersToQtModifiers(uint32_t modifiers); + + QWaylandInputMethodEventBuilder m_builder; + + QList<Qt::KeyboardModifier> m_modifiersMap; + + uint32_t m_serial = 0; + struct ::wl_surface *m_surface = nullptr; + struct ::wl_seat *m_seat = nullptr; + + QString m_preeditCommit; + + bool m_inputPanelVisible = false; + QRectF m_keyboardRectangle; + QLocale m_locale; + Qt::LayoutDirection m_inputDirection = Qt::LayoutDirectionAuto; + + struct ::wl_callback *m_resetCallback = nullptr; + static const wl_callback_listener callbackListener; + static void resetCallback(void *data, struct wl_callback *wl_callback, uint32_t time); +}; + +} + +QT_END_NAMESPACE +#endif // QWAYLANDTEXTINPUTV1_H + diff --git a/src/client/qwaylandtextinputv2.cpp b/src/client/qwaylandtextinputv2.cpp new file mode 100644 index 000000000..9d462f7d6 --- /dev/null +++ b/src/client/qwaylandtextinputv2.cpp @@ -0,0 +1,375 @@ +// Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <qpa/qplatforminputcontext.h> + +#include "qwaylandtextinputv2_p.h" + +#include "qwaylandwindow_p.h" +#include "qwaylandinputmethodeventbuilder_p.h" + +#include <QtCore/qloggingcategory.h> +#include <QtGui/QGuiApplication> +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/private/qhighdpiscaling_p.h> +#include <QtGui/qpa/qplatformintegration.h> +#include <QtGui/qevent.h> +#include <QtGui/qwindow.h> +#include <QTextCharFormat> +#include <QList> +#include <QRectF> +#include <QLocale> + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(qLcQpaInputMethods) + +namespace QtWaylandClient { + +namespace { + +const Qt::InputMethodQueries supportedQueries2 = Qt::ImEnabled | + Qt::ImSurroundingText | + Qt::ImCursorPosition | + Qt::ImAnchorPosition | + Qt::ImHints | + Qt::ImCursorRectangle | + Qt::ImPreferredLanguage; +} + +QWaylandTextInputv2::QWaylandTextInputv2(QWaylandDisplay *display, struct ::zwp_text_input_v2 *text_input) + : QtWayland::zwp_text_input_v2(text_input) + , m_display(display) +{ +} + +QWaylandTextInputv2::~QWaylandTextInputv2() +{ + if (m_resetCallback) + wl_callback_destroy(m_resetCallback); + destroy(); +} + +void QWaylandTextInputv2::reset() +{ + m_builder.reset(); + m_preeditCommit = QString(); + updateState(Qt::ImQueryAll, QtWayland::zwp_text_input_v2::update_state_reset); +} + +void QWaylandTextInputv2::commit() +{ + if (QObject *o = QGuiApplication::focusObject()) { + if (!m_preeditCommit.isEmpty()) { + + QInputMethodEvent event; + event.setCommitString(m_preeditCommit); + m_preeditCommit = QString(); + + QCoreApplication::sendEvent(o, &event); + } + } + + reset(); +} + +const wl_callback_listener QWaylandTextInputv2::callbackListener = { + QWaylandTextInputv2::resetCallback +}; + +void QWaylandTextInputv2::resetCallback(void *data, wl_callback *, uint32_t) +{ + QWaylandTextInputv2 *self = static_cast<QWaylandTextInputv2*>(data); + + if (self->m_resetCallback) { + wl_callback_destroy(self->m_resetCallback); + self->m_resetCallback = nullptr; + } +} + +void QWaylandTextInputv2::updateState(Qt::InputMethodQueries queries, uint32_t flags) +{ + if (!QGuiApplication::focusObject()) + return; + + if (!QGuiApplication::focusWindow() || !QGuiApplication::focusWindow()->handle()) + return; + + auto *window = static_cast<QWaylandWindow *>(QGuiApplication::focusWindow()->handle()); + auto *surface = window->wlSurface(); + if (!surface || (surface != m_surface)) + return; + + queries &= supportedQueries2; + + // Surrounding text, cursor and anchor positions are transferred together + if ((queries & Qt::ImSurroundingText) || (queries & Qt::ImCursorPosition) || (queries & Qt::ImAnchorPosition)) + queries |= Qt::ImSurroundingText | Qt::ImCursorPosition | Qt::ImAnchorPosition; + + QInputMethodQueryEvent event(queries); + QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event); + + if ((queries & Qt::ImSurroundingText) || (queries & Qt::ImCursorPosition) || (queries & Qt::ImAnchorPosition)) { + QString text = event.value(Qt::ImSurroundingText).toString(); + int cursor = event.value(Qt::ImCursorPosition).toInt(); + int anchor = event.value(Qt::ImAnchorPosition).toInt(); + + // Make sure text is not too big + if (text.toUtf8().size() > 2048) { + int c = qAbs(cursor - anchor) <= 512 ? qMin(cursor, anchor) + qAbs(cursor - anchor) / 2: cursor; + + const int offset = c - qBound(0, c, 512 - qMin(text.size() - c, 256)); + text = text.mid(offset + c - 256, 512); + cursor -= offset; + anchor -= offset; + } + + set_surrounding_text(text, QWaylandInputMethodEventBuilder::indexToWayland(text, cursor), QWaylandInputMethodEventBuilder::indexToWayland(text, anchor)); + } + + if (queries & Qt::ImHints) { + QWaylandInputMethodContentType contentType = QWaylandInputMethodContentType::convert(static_cast<Qt::InputMethodHints>(event.value(Qt::ImHints).toInt())); + set_content_type(contentType.hint, contentType.purpose); + } + + if (queries & Qt::ImCursorRectangle) { + const QRect &cRect = event.value(Qt::ImCursorRectangle).toRect(); + const QRect &windowRect = QGuiApplication::inputMethod()->inputItemTransform().mapRect(cRect); + const QRect &nativeRect = QHighDpi::toNativePixels(windowRect, QGuiApplication::focusWindow()); + const QMargins margins = window->clientSideMargins(); + const QRect &surfaceRect = nativeRect.translated(margins.left(), margins.top()); + set_cursor_rectangle(surfaceRect.x(), surfaceRect.y(), surfaceRect.width(), surfaceRect.height()); + } + + if (queries & Qt::ImPreferredLanguage) { + const QString &language = event.value(Qt::ImPreferredLanguage).toString(); + set_preferred_language(language); + } + + update_state(m_serial, flags); + if (flags != QtWayland::zwp_text_input_v2::update_state_change) { + if (m_resetCallback) + wl_callback_destroy(m_resetCallback); + m_resetCallback = wl_display_sync(m_display->wl_display()); + wl_callback_add_listener(m_resetCallback, &QWaylandTextInputv2::callbackListener, this); + } +} + +void QWaylandTextInputv2::setCursorInsidePreedit(int) +{ + // Not supported yet +} + +bool QWaylandTextInputv2::isInputPanelVisible() const +{ + return m_inputPanelVisible; +} + +QRectF QWaylandTextInputv2::keyboardRect() const +{ + return m_keyboardRectangle; +} + +QLocale QWaylandTextInputv2::locale() const +{ + return m_locale; +} + +Qt::LayoutDirection QWaylandTextInputv2::inputDirection() const +{ + return m_inputDirection; +} + +void QWaylandTextInputv2::zwp_text_input_v2_enter(uint32_t serial, ::wl_surface *surface) +{ + m_serial = serial; + m_surface = surface; + + updateState(Qt::ImQueryAll, QtWayland::zwp_text_input_v2::update_state_enter); +} + +void QWaylandTextInputv2::zwp_text_input_v2_leave(uint32_t serial, ::wl_surface *surface) +{ + m_serial = serial; + + if (m_surface != surface) { + qCDebug(qLcQpaInputMethods()) << Q_FUNC_INFO << "Got leave event for surface" << surface << "focused surface" << m_surface; + } + + m_surface = nullptr; +} + +void QWaylandTextInputv2::zwp_text_input_v2_modifiers_map(wl_array *map) +{ + const QList<QByteArray> modifiersMap = QByteArray::fromRawData(static_cast<const char*>(map->data), map->size).split('\0'); + + m_modifiersMap.clear(); + + for (const QByteArray &modifier : modifiersMap) { + if (modifier == "Shift") + m_modifiersMap.append(Qt::ShiftModifier); + else if (modifier == "Control") + m_modifiersMap.append(Qt::ControlModifier); + else if (modifier == "Alt") + m_modifiersMap.append(Qt::AltModifier); + else if (modifier == "Mod1") + m_modifiersMap.append(Qt::AltModifier); + else if (modifier == "Mod4") + m_modifiersMap.append(Qt::MetaModifier); + else + m_modifiersMap.append(Qt::NoModifier); + } +} + +void QWaylandTextInputv2::zwp_text_input_v2_input_panel_state(uint32_t visible, int32_t x, int32_t y, int32_t width, int32_t height) +{ + const bool inputPanelVisible = (visible == input_panel_visibility_visible); + if (m_inputPanelVisible != inputPanelVisible) { + m_inputPanelVisible = inputPanelVisible; + QGuiApplicationPrivate::platformIntegration()->inputContext()->emitInputPanelVisibleChanged(); + } + const QRectF keyboardRectangle(x, y, width, height); + if (m_keyboardRectangle != keyboardRectangle) { + m_keyboardRectangle = keyboardRectangle; + QGuiApplicationPrivate::platformIntegration()->inputContext()->emitKeyboardRectChanged(); + } +} + +void QWaylandTextInputv2::zwp_text_input_v2_preedit_string(const QString &text, const QString &commit) +{ + if (m_resetCallback) { + qCDebug(qLcQpaInputMethods()) << "discard preedit_string: reset not confirmed"; + m_builder.reset(); + return; + } + + if (!QGuiApplication::focusObject()) + return; + + QInputMethodEvent *event = m_builder.buildPreedit(text); + + m_builder.reset(); + m_preeditCommit = commit; + + QCoreApplication::sendEvent(QGuiApplication::focusObject(), event); + delete event; +} + +void QWaylandTextInputv2::zwp_text_input_v2_preedit_styling(uint32_t index, uint32_t length, uint32_t style) +{ + m_builder.addPreeditStyling(index, length, style); +} + +void QWaylandTextInputv2::zwp_text_input_v2_preedit_cursor(int32_t index) +{ + m_builder.setPreeditCursor(index); +} + +void QWaylandTextInputv2::zwp_text_input_v2_commit_string(const QString &text) +{ + if (m_resetCallback) { + qCDebug(qLcQpaInputMethods()) << "discard commit_string: reset not confirmed"; + m_builder.reset(); + return; + } + + if (!QGuiApplication::focusObject()) + return; + + QInputMethodEvent *event = m_builder.buildCommit(text); + + m_builder.reset(); + + QCoreApplication::sendEvent(QGuiApplication::focusObject(), event); + delete event; +} + +void QWaylandTextInputv2::zwp_text_input_v2_cursor_position(int32_t index, int32_t anchor) +{ + m_builder.setCursorPosition(index, anchor); +} + +void QWaylandTextInputv2::zwp_text_input_v2_delete_surrounding_text(uint32_t before_length, uint32_t after_length) +{ + m_builder.setDeleteSurroundingText(before_length, after_length); +} + +void QWaylandTextInputv2::zwp_text_input_v2_keysym(uint32_t time, uint32_t sym, uint32_t state, uint32_t modifiers) +{ +#if QT_CONFIG(xkbcommon) + if (m_resetCallback) { + qCDebug(qLcQpaInputMethods()) << "discard keysym: reset not confirmed"; + return; + } + + if (!QGuiApplication::focusWindow()) + return; + + Qt::KeyboardModifiers qtModifiers = modifiersToQtModifiers(modifiers); + + QEvent::Type type = state == WL_KEYBOARD_KEY_STATE_PRESSED ? QEvent::KeyPress : QEvent::KeyRelease; + QString text = QXkbCommon::lookupStringNoKeysymTransformations(sym); + int qtkey = QXkbCommon::keysymToQtKey(sym, qtModifiers); + + QWindowSystemInterface::handleKeyEvent(QGuiApplication::focusWindow(), + time, type, qtkey, qtModifiers, text); +#else + Q_UNUSED(time); + Q_UNUSED(sym); + Q_UNUSED(state); + Q_UNUSED(modifiers); +#endif +} + +void QWaylandTextInputv2::zwp_text_input_v2_language(const QString &language) +{ + if (m_resetCallback) { + qCDebug(qLcQpaInputMethods()) << "discard language: reset not confirmed"; + return; + } + + const QLocale locale(language); + if (m_locale != locale) { + m_locale = locale; + QGuiApplicationPrivate::platformIntegration()->inputContext()->emitLocaleChanged(); + } +} + +void QWaylandTextInputv2::zwp_text_input_v2_text_direction(uint32_t direction) +{ + if (m_resetCallback) { + qCDebug(qLcQpaInputMethods()) << "discard text_direction: reset not confirmed"; + return; + } + + const Qt::LayoutDirection inputDirection = (direction == text_direction_auto) ? Qt::LayoutDirectionAuto : + (direction == text_direction_ltr) ? Qt::LeftToRight : + (direction == text_direction_rtl) ? Qt::RightToLeft : Qt::LayoutDirectionAuto; + if (m_inputDirection != inputDirection) { + m_inputDirection = inputDirection; + QGuiApplicationPrivate::platformIntegration()->inputContext()->emitInputDirectionChanged(m_inputDirection); + } +} + +void QWaylandTextInputv2::zwp_text_input_v2_input_method_changed(uint32_t serial, uint32_t flags) +{ + Q_UNUSED(flags); + + m_serial = serial; + updateState(Qt::ImQueryAll, QtWayland::zwp_text_input_v2::update_state_full); +} + +Qt::KeyboardModifiers QWaylandTextInputv2::modifiersToQtModifiers(uint32_t modifiers) +{ + Qt::KeyboardModifiers ret = Qt::NoModifier; + for (int i = 0; i < m_modifiersMap.size(); ++i) { + if (modifiers & (1 << i)) { + ret |= m_modifiersMap[i]; + } + } + return ret; +} + +} + +QT_END_NAMESPACE diff --git a/src/client/qwaylandtextinputv2_p.h b/src/client/qwaylandtextinputv2_p.h new file mode 100644 index 000000000..94b0408a9 --- /dev/null +++ b/src/client/qwaylandtextinputv2_p.h @@ -0,0 +1,110 @@ +// Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + + +#ifndef QWAYLANDINPUTV2_P_H +#define QWAYLANDINPUTV2_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qwaylandtextinputinterface_p.h" +#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h> +#include <qwaylandinputmethodeventbuilder_p.h> + +struct wl_callback; +struct wl_callback_listener; + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class QWaylandDisplay; + +class QWaylandTextInputv2 : public QtWayland::zwp_text_input_v2, public QWaylandTextInputInterface +{ +public: + QWaylandTextInputv2(QWaylandDisplay *display, struct ::zwp_text_input_v2 *text_input); + ~QWaylandTextInputv2() override; + + void reset() override; + void commit() override; + void updateState(Qt::InputMethodQueries queries, uint32_t flags) override; + + void setCursorInsidePreedit(int cursor) override; + + bool isInputPanelVisible() const override; + QRectF keyboardRect() const override; + + QLocale locale() const override; + Qt::LayoutDirection inputDirection() const override; + + void showInputPanel() override + { + show_input_panel(); + } + void hideInputPanel() override + { + hide_input_panel(); + } + void enableSurface(::wl_surface *surface) override + { + enable(surface); + } + void disableSurface(::wl_surface *surface) override + { + disable(surface); + } + +protected: + void zwp_text_input_v2_enter(uint32_t serial, struct ::wl_surface *surface) override; + void zwp_text_input_v2_leave(uint32_t serial, struct ::wl_surface *surface) override; + void zwp_text_input_v2_modifiers_map(wl_array *map) override; + void zwp_text_input_v2_input_panel_state(uint32_t state, int32_t x, int32_t y, int32_t width, int32_t height) override; + void zwp_text_input_v2_preedit_string(const QString &text, const QString &commit) override; + void zwp_text_input_v2_preedit_styling(uint32_t index, uint32_t length, uint32_t style) override; + void zwp_text_input_v2_preedit_cursor(int32_t index) override; + void zwp_text_input_v2_commit_string(const QString &text) override; + void zwp_text_input_v2_cursor_position(int32_t index, int32_t anchor) override; + void zwp_text_input_v2_delete_surrounding_text(uint32_t before_length, uint32_t after_length) override; + void zwp_text_input_v2_keysym(uint32_t time, uint32_t sym, uint32_t state, uint32_t modifiers) override; + void zwp_text_input_v2_language(const QString &language) override; + void zwp_text_input_v2_text_direction(uint32_t direction) override; + void zwp_text_input_v2_input_method_changed(uint32_t serial, uint32_t flags) override; + +private: + Qt::KeyboardModifiers modifiersToQtModifiers(uint32_t modifiers); + + QWaylandDisplay *m_display = nullptr; + QWaylandInputMethodEventBuilder m_builder; + + QList<Qt::KeyboardModifier> m_modifiersMap; + + uint32_t m_serial = 0; + struct ::wl_surface *m_surface = nullptr; + + QString m_preeditCommit; + + bool m_inputPanelVisible = false; + QRectF m_keyboardRectangle; + QLocale m_locale; + Qt::LayoutDirection m_inputDirection = Qt::LayoutDirectionAuto; + + struct ::wl_callback *m_resetCallback = nullptr; + static const wl_callback_listener callbackListener; + static void resetCallback(void *data, struct wl_callback *wl_callback, uint32_t time); +}; + +} + +QT_END_NAMESPACE + +#endif // QWAYLANDTEXTINPUTV2_P_H diff --git a/src/client/qwaylandtextinputv3.cpp b/src/client/qwaylandtextinputv3.cpp new file mode 100644 index 000000000..bb449c9d6 --- /dev/null +++ b/src/client/qwaylandtextinputv3.cpp @@ -0,0 +1,372 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwaylandtextinputv3_p.h" + +#include "qwaylandwindow_p.h" +#include "qwaylandinputmethodeventbuilder_p.h" + +#include <QtCore/qloggingcategory.h> +#include <QtGui/qguiapplication.h> +#include <QtGui/private/qhighdpiscaling_p.h> +#include <QtGui/qevent.h> +#include <QtGui/qwindow.h> +#include <QTextCharFormat> + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(qLcQpaWaylandTextInput, "qt.qpa.wayland.textinput") + +namespace QtWaylandClient { + +QWaylandTextInputv3::QWaylandTextInputv3(QWaylandDisplay *display, + struct ::zwp_text_input_v3 *text_input) + : QtWayland::zwp_text_input_v3(text_input) +{ + Q_UNUSED(display) +} + +QWaylandTextInputv3::~QWaylandTextInputv3() +{ + destroy(); +} + +namespace { +const Qt::InputMethodQueries supportedQueries3 = Qt::ImEnabled | + Qt::ImSurroundingText | + Qt::ImCursorPosition | + Qt::ImAnchorPosition | + Qt::ImHints | + Qt::ImCursorRectangle; +} + +void QWaylandTextInputv3::zwp_text_input_v3_enter(struct ::wl_surface *surface) +{ + qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << m_surface << surface; + + m_surface = surface; + + m_pendingPreeditString.clear(); + m_pendingCommitString.clear(); + m_pendingDeleteBeforeText = 0; + m_pendingDeleteAfterText = 0; + + enable(); + updateState(supportedQueries3, update_state_enter); +} + +void QWaylandTextInputv3::zwp_text_input_v3_leave(struct ::wl_surface *surface) +{ + qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO; + + if (m_surface != surface) { + qCWarning(qLcQpaWaylandTextInput()) << Q_FUNC_INFO << "Got leave event for surface" << surface << "focused surface" << m_surface; + return; + } + + m_currentPreeditString.clear(); + + m_surface = nullptr; + + disable(); + qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "Done"; +} + +void QWaylandTextInputv3::zwp_text_input_v3_preedit_string(const QString &text, int32_t cursorBegin, int32_t cursorEnd) +{ + qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << text << cursorBegin << cursorEnd; + + if (!QGuiApplication::focusObject()) + return; + + m_pendingPreeditString.text = text; + m_pendingPreeditString.cursorBegin = QWaylandInputMethodEventBuilder::indexFromWayland(text, cursorBegin); + m_pendingPreeditString.cursorEnd = QWaylandInputMethodEventBuilder::indexFromWayland(text, cursorEnd); +} + +void QWaylandTextInputv3::zwp_text_input_v3_commit_string(const QString &text) +{ + qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << text; + + if (!QGuiApplication::focusObject()) + return; + + m_pendingCommitString = text; +} + +void QWaylandTextInputv3::zwp_text_input_v3_delete_surrounding_text(uint32_t beforeText, uint32_t afterText) +{ + qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << beforeText << afterText; + + if (!QGuiApplication::focusObject()) + return; + + m_pendingDeleteBeforeText = beforeText; + m_pendingDeleteAfterText = afterText; +} + +void QWaylandTextInputv3::zwp_text_input_v3_done(uint32_t serial) +{ + qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "with serial" << serial << m_currentSerial; + + // This is a case of double click. + // text_input_v3 will ignore this done signal and just keep the selection of the clicked word. + if (m_cursorPos != m_anchorPos && (m_pendingDeleteBeforeText != 0 || m_pendingDeleteAfterText != 0)) { + qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "Ignore done"; + m_pendingDeleteBeforeText = 0; + m_pendingDeleteAfterText = 0; + m_pendingPreeditString.clear(); + m_pendingCommitString.clear(); + return; + } + + QObject *focusObject = QGuiApplication::focusObject(); + if (!focusObject) + return; + + if (!m_surface) { + qCWarning(qLcQpaWaylandTextInput) << Q_FUNC_INFO << serial << "Surface is not enabled yet"; + return; + } + + qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "PREEDIT" << m_pendingPreeditString.text << m_pendingPreeditString.cursorBegin; + + QList<QInputMethodEvent::Attribute> attributes; + { + if (m_pendingPreeditString.cursorBegin != -1 || + m_pendingPreeditString.cursorEnd != -1) { + // Current supported cursor shape is just line. + // It means, cursorEnd and cursorBegin are the same. + QInputMethodEvent::Attribute attribute1(QInputMethodEvent::Cursor, + m_pendingPreeditString.text.length(), + 1); + attributes.append(attribute1); + } + + // only use single underline style for now + QTextCharFormat format; + format.setFontUnderline(true); + format.setUnderlineStyle(QTextCharFormat::SingleUnderline); + QInputMethodEvent::Attribute attribute2(QInputMethodEvent::TextFormat, + 0, + m_pendingPreeditString.text.length(), format); + attributes.append(attribute2); + } + QInputMethodEvent event(m_pendingPreeditString.text, attributes); + + qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "DELETE" << m_pendingDeleteBeforeText << m_pendingDeleteAfterText; + qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "COMMIT" << m_pendingCommitString; + + int replaceFrom = 0; + int replaceLength = 0; + if (m_pendingDeleteBeforeText != 0 || m_pendingDeleteAfterText != 0) { + // A workaround for reselection + // It will disable redundant commit after reselection + m_condReselection = true; + const QByteArray &utf8 = QStringView{m_surroundingText}.toUtf8(); + if (m_cursorPos < int(m_pendingDeleteBeforeText)) { + replaceFrom = -QString::fromUtf8(QByteArrayView{utf8}.first(m_pendingDeleteBeforeText)).size(); + replaceLength = QString::fromUtf8(QByteArrayView{utf8}.first(m_pendingDeleteBeforeText + m_pendingDeleteAfterText)).size(); + } else { + replaceFrom = -QString::fromUtf8(QByteArrayView{utf8}.sliced(m_cursorPos - m_pendingDeleteBeforeText, m_pendingDeleteBeforeText)).size(); + replaceLength = QString::fromUtf8(QByteArrayView{utf8}.sliced(m_cursorPos - m_pendingDeleteBeforeText, m_pendingDeleteBeforeText + m_pendingDeleteAfterText)).size(); + } + } + + qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "DELETE from " << replaceFrom << " length " << replaceLength; + event.setCommitString(m_pendingCommitString, + replaceFrom, + replaceLength); + m_currentPreeditString = m_pendingPreeditString; + m_pendingPreeditString.clear(); + m_pendingCommitString.clear(); + m_pendingDeleteBeforeText = 0; + m_pendingDeleteAfterText = 0; + QCoreApplication::sendEvent(focusObject, &event); + + if (serial == m_currentSerial) + updateState(supportedQueries3, update_state_full); +} + +void QWaylandTextInputv3::reset() +{ + qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO; + + m_pendingPreeditString.clear(); +} + +void QWaylandTextInputv3::commit() +{ + m_currentSerial = (m_currentSerial < UINT_MAX) ? m_currentSerial + 1U: 0U; + + qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "with serial" << m_currentSerial; + QtWayland::zwp_text_input_v3::commit(); +} + +void QWaylandTextInputv3::updateState(Qt::InputMethodQueries queries, uint32_t flags) +{ + qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << queries << flags; + + if (!QGuiApplication::focusObject()) + return; + + if (!QGuiApplication::focusWindow() || !QGuiApplication::focusWindow()->handle()) + return; + + auto *window = static_cast<QWaylandWindow *>(QGuiApplication::focusWindow()->handle()); + auto *surface = window->wlSurface(); + if (!surface || (surface != m_surface)) + return; + + queries &= supportedQueries3; + bool needsCommit = false; + + QInputMethodQueryEvent event(queries); + QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event); + + // For some reason, a query for Qt::ImSurroundingText gives an empty string even though it is not. + if (!(queries & Qt::ImSurroundingText) && event.value(Qt::ImSurroundingText).toString().isEmpty()) { + return; + } + + if (queries & Qt::ImCursorRectangle) { + const QRect &cRect = event.value(Qt::ImCursorRectangle).toRect(); + const QRect &windowRect = QGuiApplication::inputMethod()->inputItemTransform().mapRect(cRect); + const QRect &nativeRect = QHighDpi::toNativePixels(windowRect, QGuiApplication::focusWindow()); + const QMargins margins = window->clientSideMargins(); + const QRect &surfaceRect = nativeRect.translated(margins.left(), margins.top()); + if (surfaceRect != m_cursorRect) { + set_cursor_rectangle(surfaceRect.x(), surfaceRect.y(), surfaceRect.width(), surfaceRect.height()); + m_cursorRect = surfaceRect; + needsCommit = true; + } + } + + if ((queries & Qt::ImSurroundingText) || (queries & Qt::ImCursorPosition) || (queries & Qt::ImAnchorPosition)) { + QString text = event.value(Qt::ImSurroundingText).toString(); + int cursor = event.value(Qt::ImCursorPosition).toInt(); + int anchor = event.value(Qt::ImAnchorPosition).toInt(); + + qCDebug(qLcQpaWaylandTextInput) << "Original surrounding_text from InputMethodQuery: " << text << cursor << anchor; + + // Make sure text is not too big + // surround_text cannot exceed 4000byte in wayland protocol + // The worst case will be supposed here. + const int MAX_MESSAGE_SIZE = 4000; + + const int textSize = text.toUtf8().size(); + if (textSize > MAX_MESSAGE_SIZE) { + qCDebug(qLcQpaWaylandTextInput) << "SurroundText size is over " + << MAX_MESSAGE_SIZE + << " byte, some text will be clipped."; + const int selectionStart = qMin(cursor, anchor); + const int selectionEnd = qMax(cursor, anchor); + const int selectionLength = selectionEnd - selectionStart; + const int selectionSize = QStringView{text}.sliced(selectionStart, selectionLength).toUtf8().size(); + // If selection is bigger than 4000 byte, it is fixed to 4000 byte. + // anchor will be moved in the 4000 byte boundary. + if (selectionSize > MAX_MESSAGE_SIZE) { + if (anchor > cursor) { + cursor = 0; + anchor = MAX_MESSAGE_SIZE; + text = text.sliced(selectionStart, selectionLength); + } else { + anchor = 0; + cursor = MAX_MESSAGE_SIZE; + text = text.sliced(selectionEnd - selectionLength, selectionLength); + } + } else { + // This is not optimal in some cases. + // For examples, if the cursor position and + // the selectionEnd are close to the end of the surround text, + // the tail of the text might always be clipped. + // However all the cases of over 4000 byte are just exceptions. + int selEndSize = QStringView{text}.first(selectionEnd).toUtf8().size(); + cursor = QWaylandInputMethodEventBuilder::indexToWayland(text, cursor); + anchor = QWaylandInputMethodEventBuilder::indexToWayland(text, anchor); + if (selEndSize < MAX_MESSAGE_SIZE) { + text = QString::fromUtf8(QByteArrayView{text.toUtf8()}.first(MAX_MESSAGE_SIZE)); + } else { + const int startOffset = selEndSize - MAX_MESSAGE_SIZE; + text = QString::fromUtf8(QByteArrayView{text.toUtf8()}.sliced(startOffset, MAX_MESSAGE_SIZE)); + cursor -= startOffset; + anchor -= startOffset; + } + } + } else { + cursor = QWaylandInputMethodEventBuilder::indexToWayland(text, cursor); + anchor = QWaylandInputMethodEventBuilder::indexToWayland(text, anchor); + } + qCDebug(qLcQpaWaylandTextInput) << "Modified surrounding_text: " << text << cursor << anchor; + + if (m_surroundingText != text || m_cursorPos != cursor || m_anchorPos != anchor) { + qCDebug(qLcQpaWaylandTextInput) << "Current surrounding_text: " << m_surroundingText << m_cursorPos << m_anchorPos; + qCDebug(qLcQpaWaylandTextInput) << "New surrounding_text: " << text << cursor << anchor; + + set_surrounding_text(text, cursor, anchor); + + // A workaround in the case of reselection + // It will work when re-clicking a preedit text + if (m_condReselection) { + qCDebug(qLcQpaWaylandTextInput) << "\"commit\" is disabled when Reselection by changing focus"; + m_condReselection = false; + needsCommit = false; + + } + + m_surroundingText = text; + m_cursorPos = cursor; + m_anchorPos = anchor; + m_cursor = cursor; + } + } + + if (queries & Qt::ImHints) { + QWaylandInputMethodContentType contentType = QWaylandInputMethodContentType::convertV3(static_cast<Qt::InputMethodHints>(event.value(Qt::ImHints).toInt())); + qCDebug(qLcQpaWaylandTextInput) << m_contentHint << contentType.hint; + qCDebug(qLcQpaWaylandTextInput) << m_contentPurpose << contentType.purpose; + + if (m_contentHint != contentType.hint || m_contentPurpose != contentType.purpose) { + qCDebug(qLcQpaWaylandTextInput) << "set_content_type: " << contentType.hint << contentType.purpose; + set_content_type(contentType.hint, contentType.purpose); + + m_contentHint = contentType.hint; + m_contentPurpose = contentType.purpose; + needsCommit = true; + } + } + + if (needsCommit + && (flags == update_state_change || flags == update_state_enter)) + commit(); +} + +void QWaylandTextInputv3::setCursorInsidePreedit(int cursor) +{ + Q_UNUSED(cursor); +} + +bool QWaylandTextInputv3::isInputPanelVisible() const +{ + return false; +} + +QRectF QWaylandTextInputv3::keyboardRect() const +{ + qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO; + return m_cursorRect; +} + +QLocale QWaylandTextInputv3::locale() const +{ + return QLocale(); +} + +Qt::LayoutDirection QWaylandTextInputv3::inputDirection() const +{ + return Qt::LeftToRight; +} + +} + +QT_END_NAMESPACE diff --git a/src/client/qwaylandtextinputv3_p.h b/src/client/qwaylandtextinputv3_p.h new file mode 100644 index 000000000..8e32e514d --- /dev/null +++ b/src/client/qwaylandtextinputv3_p.h @@ -0,0 +1,102 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWAYLANDTEXTINPUTV3_P_H +#define QWAYLANDTEXTINPUTV3_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qwaylandtextinputinterface_p.h" +#include <QtWaylandClient/private/qwayland-text-input-unstable-v3.h> +#include <QLoggingCategory> + +struct wl_callback; +struct wl_callback_listener; + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(qLcQpaWaylandTextInput) + +namespace QtWaylandClient { + +class QWaylandDisplay; + +class QWaylandTextInputv3 : public QtWayland::zwp_text_input_v3, public QWaylandTextInputInterface +{ +public: + QWaylandTextInputv3(QWaylandDisplay *display, struct ::zwp_text_input_v3 *text_input); + ~QWaylandTextInputv3() override; + + void reset() override; + void commit() override; + void updateState(Qt::InputMethodQueries queries, uint32_t flags) override; + // TODO: not supported yet + void setCursorInsidePreedit(int cursor) override; + + bool isInputPanelVisible() const override; + QRectF keyboardRect() const override; + + QLocale locale() const override; + Qt::LayoutDirection inputDirection() const override; + + // doing nothing in zwp_text_input_v3. + // enter() and leave() takes the role to enable/disable the surface + void enableSurface(::wl_surface *) override {}; + void disableSurface(::wl_surface *) override {}; + +protected: + void zwp_text_input_v3_enter(struct ::wl_surface *surface) override; + void zwp_text_input_v3_leave(struct ::wl_surface *surface) override; + void zwp_text_input_v3_preedit_string(const QString &text, int32_t cursor_begin, int32_t cursor_end) override; + void zwp_text_input_v3_commit_string(const QString &text) override; + void zwp_text_input_v3_delete_surrounding_text(uint32_t before_length, uint32_t after_length) override; + void zwp_text_input_v3_done(uint32_t serial) override; + +private: + ::wl_surface *m_surface = nullptr; // ### Here for debugging purposes + + struct PreeditInfo { + QString text; + int cursorBegin = 0; + int cursorEnd = 0; + + void clear() { + text.clear(); + cursorBegin = 0; + cursorEnd = 0; + } + }; + + PreeditInfo m_pendingPreeditString; + PreeditInfo m_currentPreeditString; + QString m_pendingCommitString; + uint m_pendingDeleteBeforeText = 0; // byte length + uint m_pendingDeleteAfterText = 0; // byte length + + QString m_surroundingText; + int m_cursor; // cursor position in QString + int m_cursorPos; // cursor position in wayland index + int m_anchorPos; // anchor position in wayland index + uint32_t m_contentHint = 0; + uint32_t m_contentPurpose = 0; + QRect m_cursorRect; + + uint m_currentSerial = 0; + + bool m_condReselection = false; +}; + +} + +QT_END_NAMESPACE + +#endif // QWAYLANDTEXTINPUTV3_P_H diff --git a/src/client/qwaylandtouch.cpp b/src/client/qwaylandtouch.cpp index 0394aef31..dfd04d446 100644 --- a/src/client/qwaylandtouch.cpp +++ b/src/client/qwaylandtouch.cpp @@ -1,47 +1,13 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandtouch_p.h" #include "qwaylandinputdevice_p.h" #include "qwaylanddisplay_p.h" #include "qwaylandsurface_p.h" +#include <QtGui/QPointingDevice> + QT_BEGIN_NAMESPACE namespace QtWaylandClient { @@ -57,12 +23,18 @@ QWaylandTouchExtension::QWaylandTouchExtension(QWaylandDisplay *display, uint32_ { } +QWaylandTouchExtension::~QWaylandTouchExtension() +{ + qt_touch_extension_destroy(object()); +} + void QWaylandTouchExtension::registerDevice(int caps) { - mTouchDevice = new QTouchDevice; - mTouchDevice->setType(QTouchDevice::TouchScreen); - mTouchDevice->setCapabilities(QTouchDevice::Capabilities(caps)); - QWindowSystemInterface::registerTouchDevice(mTouchDevice); + // TODO number of touchpoints, actual name and ID + mTouchDevice = new QPointingDevice(QLatin1String("some touchscreen"), 0, + QInputDevice::DeviceType::TouchScreen, QPointingDevice::PointerType::Finger, + QInputDevice::Capabilities(caps), 10, 0); + QWindowSystemInterface::registerInputDevice(mTouchDevice); } static inline qreal fromFixed(int f) @@ -98,13 +70,12 @@ void QWaylandTouchExtension::touch_extension_touch(uint32_t time, QWindowSystemInterface::TouchPoint tp; tp.id = id; - tp.state = Qt::TouchPointState(int(state & 0xFFFF)); + tp.state = QEventPoint::State(int(state & 0xFFFF)); int sentPointCount = state >> 16; if (!mPointsLeft) { Q_ASSERT(sentPointCount > 0); mPointsLeft = sentPointCount; } - tp.flags = QTouchEvent::TouchPoint::InfoFlags(int(flags & 0xFFFF)); if (!mTouchDevice) registerDevice(flags >> 16); @@ -141,19 +112,19 @@ void QWaylandTouchExtension::touch_extension_touch(uint32_t time, void QWaylandTouchExtension::sendTouchEvent() { // Copy all points, that are in the previous but not in the current list, as stationary. - for (int i = 0; i < mPrevTouchPoints.count(); ++i) { + for (int i = 0; i < mPrevTouchPoints.size(); ++i) { const QWindowSystemInterface::TouchPoint &prevPoint(mPrevTouchPoints.at(i)); - if (prevPoint.state == Qt::TouchPointReleased) + if (prevPoint.state == QEventPoint::Released) continue; bool found = false; - for (int j = 0; j < mTouchPoints.count(); ++j) + for (int j = 0; j < mTouchPoints.size(); ++j) if (mTouchPoints.at(j).id == prevPoint.id) { found = true; break; } if (!found) { QWindowSystemInterface::TouchPoint p = prevPoint; - p.state = Qt::TouchPointStationary; + p.state = QEventPoint::Stationary; mTouchPoints.append(p); } } @@ -165,22 +136,28 @@ void QWaylandTouchExtension::sendTouchEvent() QWindowSystemInterface::handleTouchEvent(mTargetWindow, mTimestamp, mTouchDevice, mTouchPoints); - Qt::TouchPointStates states = 0; - for (int i = 0; i < mTouchPoints.count(); ++i) + QEventPoint::States states = {}; + for (int i = 0; i < mTouchPoints.size(); ++i) states |= mTouchPoints.at(i).state; if (mFlags & QT_TOUCH_EXTENSION_FLAGS_MOUSE_FROM_TOUCH) { - if (states == Qt::TouchPointPressed) + const bool firstPress = states == QEventPoint::Pressed; + if (firstPress) mMouseSourceId = mTouchPoints.first().id; - for (int i = 0; i < mTouchPoints.count(); ++i) { + for (int i = 0; i < mTouchPoints.size(); ++i) { const QWindowSystemInterface::TouchPoint &tp(mTouchPoints.at(i)); if (tp.id == mMouseSourceId) { - Qt::MouseButtons buttons = tp.state == Qt::TouchPointReleased ? Qt::NoButton : Qt::LeftButton; + const bool released = tp.state == QEventPoint::Released; + Qt::MouseButtons buttons = released ? Qt::NoButton : Qt::LeftButton; + QEvent::Type eventType = firstPress ? QEvent::MouseButtonPress + : released ? QEvent::MouseButtonRelease + : QEvent::MouseMove; mLastMouseGlobal = tp.area.center(); QPoint globalPoint = mLastMouseGlobal.toPoint(); QPointF delta = mLastMouseGlobal - globalPoint; mLastMouseLocal = mTargetWindow->mapFromGlobal(globalPoint) + delta; - QWindowSystemInterface::handleMouseEvent(mTargetWindow, mTimestamp, mLastMouseLocal, mLastMouseGlobal, buttons); + QWindowSystemInterface::handleMouseEvent(mTargetWindow, mTimestamp, mLastMouseLocal, mLastMouseGlobal, + buttons, Qt::LeftButton, eventType); if (buttons == Qt::NoButton) mMouseSourceId = -1; break; @@ -191,7 +168,7 @@ void QWaylandTouchExtension::sendTouchEvent() mPrevTouchPoints = mTouchPoints; mTouchPoints.clear(); - if (states == Qt::TouchPointReleased) + if (states == QEventPoint::Released) mPrevTouchPoints.clear(); } @@ -200,7 +177,7 @@ void QWaylandTouchExtension::touchCanceled() mTouchPoints.clear(); mPrevTouchPoints.clear(); if (mMouseSourceId != -1) - QWindowSystemInterface::handleMouseEvent(mTargetWindow, mTimestamp, mLastMouseLocal, mLastMouseGlobal, Qt::NoButton); + QWindowSystemInterface::handleMouseEvent(mTargetWindow, mTimestamp, mLastMouseLocal, mLastMouseGlobal, Qt::NoButton, Qt::LeftButton, QEvent::MouseButtonRelease); } void QWaylandTouchExtension::touch_extension_configure(uint32_t flags) diff --git a/src/client/qwaylandtouch_p.h b/src/client/qwaylandtouch_p.h index 93a829e21..e283f9009 100644 --- a/src/client/qwaylandtouch_p.h +++ b/src/client/qwaylandtouch_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDTOUCH_H #define QWAYLANDTOUCH_H @@ -55,6 +19,7 @@ #include <QtWaylandClient/private/qwayland-touch-extension.h> #include <QtWaylandClient/qtwaylandclientglobal.h> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -63,10 +28,11 @@ namespace QtWaylandClient { class QWaylandDisplay; class QWaylandInputDevice; -class Q_WAYLAND_CLIENT_EXPORT QWaylandTouchExtension : public QtWayland::qt_touch_extension +class Q_WAYLANDCLIENT_EXPORT QWaylandTouchExtension : public QtWayland::qt_touch_extension { public: QWaylandTouchExtension(QWaylandDisplay *display, uint32_t id); + ~QWaylandTouchExtension() override; void touchCanceled(); @@ -95,7 +61,7 @@ private: QList<QWindowSystemInterface::TouchPoint> mTouchPoints; QList<QWindowSystemInterface::TouchPoint> mPrevTouchPoints; - QTouchDevice *mTouchDevice = nullptr; + QPointingDevice *mTouchDevice = nullptr; uint32_t mTimestamp; int mPointsLeft; uint32_t mFlags; diff --git a/src/client/qwaylandviewport.cpp b/src/client/qwaylandviewport.cpp new file mode 100644 index 000000000..3252718c0 --- /dev/null +++ b/src/client/qwaylandviewport.cpp @@ -0,0 +1,35 @@ +// Copyright (C) 2022 David Edmundson <davidedmundson@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwaylandviewport_p.h" + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +QWaylandViewport::QWaylandViewport(::wp_viewport *viewport) + : QtWayland::wp_viewport(viewport) +{ +} + +QWaylandViewport::~QWaylandViewport() +{ + destroy(); +} + +void QWaylandViewport::setSource(const QRectF &source) +{ + set_source(wl_fixed_from_double(source.x()), + wl_fixed_from_double(source.y()), + wl_fixed_from_double(source.width()), + wl_fixed_from_double(source.height())); +} + +void QWaylandViewport::setDestination(const QSize &destination) +{ + set_destination(destination.width(), destination.height()); +} + +} + +QT_END_NAMESPACE diff --git a/src/client/qwaylandviewport_p.h b/src/client/qwaylandviewport_p.h new file mode 100644 index 000000000..e1dfeb3a7 --- /dev/null +++ b/src/client/qwaylandviewport_p.h @@ -0,0 +1,42 @@ +// Copyright (C) 2022 David Edmundson <davidedmundson@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWAYLANDVIEWPORT_P_H +#define QWAYLANDVIEWPORT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtWaylandClient/private/qwayland-viewporter.h> +#include <QtWaylandClient/qtwaylandclientglobal.h> + +#include <QRect> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class QWaylandViewport : public QtWayland::wp_viewport +{ +public: + explicit QWaylandViewport(::wp_viewport *viewport); + ~QWaylandViewport() override; + + void setSource(const QRectF &source); + void setDestination(const QSize &destination); + +}; + +} + +QT_END_NAMESPACE + +#endif // QWAYLANDVIEWPORT_P_H diff --git a/src/client/qwaylandvulkaninstance.cpp b/src/client/qwaylandvulkaninstance.cpp index 5edbd4757..be55130a4 100644 --- a/src/client/qwaylandvulkaninstance.cpp +++ b/src/client/qwaylandvulkaninstance.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandvulkaninstance_p.h" #include "qwaylandwindow_p.h" @@ -124,9 +88,39 @@ void QWaylandVulkanInstance::presentAboutToBeQueued(QWindow *window) qWarning() << "Attempted to call presentAboutToBeQueued() without a valid platform window"; return; } + + bool ok; + int frameCallbackTimeout = qEnvironmentVariableIntValue("QT_WAYLAND_FRAME_CALLBACK_TIMEOUT", &ok); + + if (ok) + mFrameCallbackTimeout = frameCallbackTimeout; + + if (w->format().swapInterval() > 0) + w->waitForFrameSync(mFrameCallbackTimeout); + w->handleUpdate(); } +void QWaylandVulkanInstance::beginFrame(QWindow *window) +{ + auto *w = static_cast<QWaylandWindow *>(window->handle()); + if (!w) { + qWarning() << "Attempted to call beginFrame() without a valid platform window"; + return; + } + w->beginFrame(); +} + +void QWaylandVulkanInstance::endFrame(QWindow *window) +{ + auto *w = static_cast<QWaylandWindow *>(window->handle()); + if (!w) { + qWarning() << "Attempted to call endFrame() without a valid platform window"; + return; + } + w->endFrame(); +} + } // namespace QtWaylandClient QT_END_NAMESPACE diff --git a/src/client/qwaylandvulkaninstance_p.h b/src/client/qwaylandvulkaninstance_p.h index b68293b78..7683ce566 100644 --- a/src/client/qwaylandvulkaninstance_p.h +++ b/src/client/qwaylandvulkaninstance_p.h @@ -1,52 +1,27 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDVULKANINSTANCE_P_H #define QWAYLANDVULKANINSTANCE_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #if defined(VULKAN_H_) && !defined(VK_USE_PLATFORM_WAYLAND_KHR) #error "vulkan.h included without Wayland WSI" #endif #define VK_USE_PLATFORM_WAYLAND_KHR -#include <QtVulkanSupport/private/qbasicvulkanplatforminstance_p.h> +#include <QtGui/private/qbasicvulkanplatforminstance_p.h> #include <QLibrary> QT_BEGIN_NAMESPACE @@ -67,10 +42,14 @@ public: VkSurfaceKHR createSurface(QWaylandWindow *window); + void beginFrame(QWindow *window) override; + void endFrame(QWindow *window) override; + private: QVulkanInstance *m_instance = nullptr; PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR m_getPhysDevPresSupport = nullptr; PFN_vkCreateWaylandSurfaceKHR m_createSurface = nullptr; + int mFrameCallbackTimeout = 100; }; } // namespace QtWaylandClient diff --git a/src/client/qwaylandvulkanwindow.cpp b/src/client/qwaylandvulkanwindow.cpp index 4c67b6b32..2bc52829d 100644 --- a/src/client/qwaylandvulkanwindow.cpp +++ b/src/client/qwaylandvulkanwindow.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandvulkanwindow_p.h" @@ -43,18 +7,14 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { -QWaylandVulkanWindow::QWaylandVulkanWindow(QWindow *window) - : QWaylandWindow(window) +QWaylandVulkanWindow::QWaylandVulkanWindow(QWindow *window, QWaylandDisplay *display) + : QWaylandWindow(window, display) { } QWaylandVulkanWindow::~QWaylandVulkanWindow() { - if (m_surface) { - QVulkanInstance *inst = window()->vulkanInstance(); - if (inst) - static_cast<QWaylandVulkanInstance *>(inst->handle())->destroySurface(m_surface); - } + invalidateSurface(); } QWaylandWindow::WindowType QWaylandVulkanWindow::windowType() const @@ -62,7 +22,18 @@ QWaylandWindow::WindowType QWaylandVulkanWindow::windowType() const return QWaylandWindow::Vulkan; } -VkSurfaceKHR *QWaylandVulkanWindow::surface() +void QWaylandVulkanWindow::invalidateSurface() +{ + if (m_surface) { + QVulkanInstance *inst = window()->vulkanInstance(); + if (inst) + static_cast<QWaylandVulkanInstance *>(inst->handle())->destroySurface(m_surface); + } + m_surface = VK_NULL_HANDLE; + QWaylandWindow::invalidateSurface(); +} + +VkSurfaceKHR *QWaylandVulkanWindow::vkSurface() { if (m_surface) return &m_surface; diff --git a/src/client/qwaylandvulkanwindow_p.h b/src/client/qwaylandvulkanwindow_p.h index d0b2de75d..c5692bc7f 100644 --- a/src/client/qwaylandvulkanwindow_p.h +++ b/src/client/qwaylandvulkanwindow_p.h @@ -1,45 +1,20 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDVULKANWINDOW_P_H #define QWAYLANDVULKANWINDOW_P_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "qwaylandwindow_p.h" #include "qwaylandvulkaninstance_p.h" @@ -50,12 +25,13 @@ namespace QtWaylandClient { class QWaylandVulkanWindow : public QWaylandWindow { public: - explicit QWaylandVulkanWindow(QWindow *window); + explicit QWaylandVulkanWindow(QWindow *window, QWaylandDisplay *display); ~QWaylandVulkanWindow() override; WindowType windowType() const override; + void invalidateSurface() override; - VkSurfaceKHR *surface(); + VkSurfaceKHR *vkSurface(); private: VkSurfaceKHR m_surface = VK_NULL_HANDLE; diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp index 950486c0c..e311b2eb4 100644 --- a/src/client/qwaylandwindow.cpp +++ b/src/client/qwaylandwindow.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the config.tests of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandwindow_p.h" @@ -43,15 +7,17 @@ #include "qwaylanddisplay_p.h" #include "qwaylandsurface_p.h" #include "qwaylandinputdevice_p.h" +#include "qwaylandfractionalscale_p.h" #include "qwaylandscreen_p.h" #include "qwaylandshellsurface_p.h" #include "qwaylandsubsurface_p.h" #include "qwaylandabstractdecoration_p.h" -#include "qwaylandwindowmanagerintegration_p.h" +#include "qwaylandplatformservices_p.h" #include "qwaylandnativeinterface_p.h" #include "qwaylanddecorationfactory_p.h" #include "qwaylandshmbackingstore_p.h" #include "qwaylandshellintegration_p.h" +#include "qwaylandviewport_p.h" #include <QtCore/QFileInfo> #include <QtCore/QPointer> @@ -60,38 +26,56 @@ #include <QGuiApplication> #include <qpa/qwindowsysteminterface.h> +#include <QtGui/private/qguiapplication_p.h> #include <QtGui/private/qwindow_p.h> #include <QtCore/QDebug> #include <QtCore/QThread> +#include <QtCore/private/qthread_p.h> + +#include <QtWaylandClient/private/qwayland-fractional-scale-v1.h> QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + namespace QtWaylandClient { Q_LOGGING_CATEGORY(lcWaylandBackingstore, "qt.qpa.wayland.backingstore") QWaylandWindow *QWaylandWindow::mMouseGrab = nullptr; +QWaylandWindow *QWaylandWindow::mTopPopup = nullptr; -QWaylandWindow::QWaylandWindow(QWindow *window) +QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display) : QPlatformWindow(window) - , mDisplay(waylandScreen()->display()) - , mFrameQueue(mDisplay->createEventQueue()) + , mDisplay(display) + , mSurfaceLock(QReadWriteLock::Recursive) + , mShellIntegration(display->shellIntegration()) , mResizeAfterSwap(qEnvironmentVariableIsSet("QT_WAYLAND_RESIZE_AFTER_SWAP")) { + { + bool ok; + int frameCallbackTimeout = qEnvironmentVariableIntValue("QT_WAYLAND_FRAME_CALLBACK_TIMEOUT", &ok); + if (ok) + mFrameCallbackTimeout = frameCallbackTimeout; + } + static WId id = 1; mWindowId = id++; initializeWlSurface(); + + connect(this, &QWaylandWindow::wlSurfaceCreated, this, + &QNativeInterface::Private::QWaylandWindow::surfaceCreated); + connect(this, &QWaylandWindow::wlSurfaceDestroyed, this, + &QNativeInterface::Private::QWaylandWindow::surfaceDestroyed); } QWaylandWindow::~QWaylandWindow() { - mDisplay->handleWindowDestroyed(this); - delete mWindowDecoration; if (mSurface) - reset(false); + reset(); const QWindow *parent = window(); const auto tlw = QGuiApplication::topLevelWindows(); @@ -107,8 +91,10 @@ QWaylandWindow::~QWaylandWindow() void QWaylandWindow::ensureSize() { - if (mBackingStore) - mBackingStore->ensureSize(); + if (mBackingStore) { + setBackingStore(mBackingStore); + mBackingStore->recreateBackBufferIfNeeded(); + } } void QWaylandWindow::initWindow() @@ -118,52 +104,76 @@ void QWaylandWindow::initWindow() if (!mSurface) { initializeWlSurface(); - QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceCreated); - QGuiApplication::sendEvent(window(), &e); + } + + if (mDisplay->fractionalScaleManager() && qApp->highDpiScaleFactorRoundingPolicy() == Qt::HighDpiScaleFactorRoundingPolicy::PassThrough) { + mFractionalScale.reset(new QWaylandFractionalScale(mDisplay->fractionalScaleManager()->get_fractional_scale(mSurface->object()))); + + connect(mFractionalScale.data(), &QWaylandFractionalScale::preferredScaleChanged, + this, &QWaylandWindow::updateScale); } if (shouldCreateSubSurface()) { Q_ASSERT(!mSubSurfaceWindow); auto *parent = static_cast<QWaylandWindow *>(QPlatformWindow::parent()); + if (!parent->mSurface) + parent->initializeWlSurface(); if (parent->wlSurface()) { if (::wl_subsurface *subsurface = mDisplay->createSubSurface(this, parent)) mSubSurfaceWindow = new QWaylandSubSurface(this, parent, subsurface); } } else if (shouldCreateShellSurface()) { Q_ASSERT(!mShellSurface); - Q_ASSERT(mDisplay->shellIntegration()); + Q_ASSERT(mShellIntegration); + mTransientParent = guessTransientParent(); + if (mTransientParent) { + if (window()->type() == Qt::Popup) { + if (mTopPopup && mTopPopup != mTransientParent) { + qCWarning(lcQpaWayland) << "Creating a popup with a parent," << mTransientParent->window() + << "which does not match the current topmost grabbing popup," + << mTopPopup->window() << "With some shell surface protocols, this" + << "is not allowed. The wayland QPA plugin is currently handling" + << "it by setting the parent to the topmost grabbing popup." + << "Note, however, that this may cause positioning errors and" + << "popups closing unxpectedly. Please fix the transient parent of the popup."; + mTransientParent = mTopPopup; + } + mTopPopup = this; + } + } - mShellSurface = mDisplay->shellIntegration()->createShellSurface(this); + mShellSurface = mShellIntegration->createShellSurface(this); if (mShellSurface) { + if (mTransientParent) { + if (window()->type() == Qt::ToolTip || window()->type() == Qt::Popup) + mTransientParent->addChildPopup(this); + } + // Set initial surface title setWindowTitle(window()->title()); // The appId is the desktop entry identifier that should follow the - // reverse DNS convention (see http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s02.html). - // According to xdg-shell the appId is only the name, without - // the .desktop suffix. + // reverse DNS convention (see + // http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s02.html). According + // to xdg-shell the appId is only the name, without the .desktop suffix. // - // If the application specifies the desktop file name use that - // removing the ".desktop" suffix, otherwise fall back to the - // executable name and prepend the reversed organization domain - // when available. + // If the application specifies the desktop file name use that, + // otherwise fall back to the executable name and prepend the + // reversed organization domain when available. if (!QGuiApplication::desktopFileName().isEmpty()) { - QString name = QGuiApplication::desktopFileName(); - if (name.endsWith(QLatin1String(".desktop"))) - name.chop(8); - mShellSurface->setAppId(name); + mShellSurface->setAppId(QGuiApplication::desktopFileName()); } else { - QFileInfo fi = QCoreApplication::instance()->applicationFilePath(); + QFileInfo fi = QFileInfo(QCoreApplication::instance()->applicationFilePath()); QStringList domainName = QCoreApplication::instance()->organizationDomain().split(QLatin1Char('.'), - QString::SkipEmptyParts); + Qt::SkipEmptyParts); if (domainName.isEmpty()) { mShellSurface->setAppId(fi.baseName()); } else { QString appId; - for (int i = 0; i < domainName.count(); ++i) + for (int i = 0; i < domainName.size(); ++i) appId.prepend(QLatin1Char('.')).prepend(domainName.at(i)); appId.append(fi.baseName()); mShellSurface->setAppId(appId); @@ -172,31 +182,43 @@ void QWaylandWindow::initWindow() // the user may have already set some window properties, so make sure to send them out for (auto it = m_properties.cbegin(); it != m_properties.cend(); ++it) mShellSurface->sendProperty(it.key(), it.value()); + + emit surfaceRoleCreated(); } else { qWarning("Could not create a shell surface object."); } } - mScale = waylandScreen()->scale(); + // The fractional scale manager check is needed to work around Gnome < 36 where viewports don't work + // Right now viewports are only necessary when a fractional scale manager is used + if (display()->viewporter() && display()->fractionalScaleManager()) { + mViewport.reset(new QWaylandViewport(display()->createViewport(this))); + } // Enable high-dpi rendering. Scale() returns the screen scale factor and will // typically be integer 1 (normal-dpi) or 2 (high-dpi). Call set_buffer_scale() // to inform the compositor that high-resolution buffers will be provided. - if (mDisplay->compositorVersion() >= 3) - mSurface->set_buffer_scale(scale()); + if (mViewport) + updateViewport(); + else if (mSurface->version() >= 3) + mSurface->set_buffer_scale(std::ceil(scale())); - if (QScreen *s = window()->screen()) - setOrientationMask(s->orientationUpdateMask()); setWindowFlags(window()->flags()); - if (window()->geometry().isEmpty()) - setGeometry_helper(QRect(QPoint(), QSize(500,500))); - else - setGeometry_helper(window()->geometry()); + QRect geometry = windowGeometry(); + QRect defaultGeometry = this->defaultGeometry(); + if (geometry.width() <= 0) + geometry.setWidth(defaultGeometry.width()); + if (geometry.height() <= 0) + geometry.setHeight(defaultGeometry.height()); + + setGeometry_helper(geometry); setMask(window()->mask()); if (mShellSurface) mShellSurface->requestWindowStates(window()->windowStates()); handleContentOrientationChange(window()->contentOrientation()); mFlags = window()->flags(); + + mSurface->commit(); } void QWaylandWindow::initializeWlSurface() @@ -207,14 +229,28 @@ void QWaylandWindow::initializeWlSurface() mSurface.reset(new QWaylandSurface(mDisplay)); connect(mSurface.data(), &QWaylandSurface::screensChanged, this, &QWaylandWindow::handleScreensChanged); + connect(mSurface.data(), &QWaylandSurface::preferredBufferScaleChanged, + this, &QWaylandWindow::updateScale); + connect(mSurface.data(), &QWaylandSurface::preferredBufferTransformChanged, + this, &QWaylandWindow::updateBufferTransform); mSurface->m_window = this; } emit wlSurfaceCreated(); } +void QWaylandWindow::setShellIntegration(QWaylandShellIntegration *shellIntegration) +{ + Q_ASSERT(shellIntegration); + if (mShellSurface) { + qCWarning(lcQpaWayland) << "Cannot set shell integration while there's already a shell surface created"; + return; + } + mShellIntegration = shellIntegration; +} + bool QWaylandWindow::shouldCreateShellSurface() const { - if (!mDisplay->shellIntegration()) + if (!shellIntegration()) return false; if (shouldCreateSubSurface()) @@ -234,36 +270,76 @@ bool QWaylandWindow::shouldCreateSubSurface() const return QPlatformWindow::parent() != nullptr; } -void QWaylandWindow::reset(bool sendDestroyEvent) +void QWaylandWindow::beginFrame() { - if (mSurface && sendDestroyEvent) { - QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed); - QGuiApplication::sendEvent(window(), &e); - } - delete mShellSurface; - mShellSurface = nullptr; - delete mSubSurfaceWindow; - mSubSurfaceWindow = nullptr; + mSurfaceLock.lockForRead(); +} + +void QWaylandWindow::endFrame() +{ + mSurfaceLock.unlock(); +} + +void QWaylandWindow::reset() +{ + closeChildPopups(); + + if (mTopPopup == this) + mTopPopup = mTransientParent && (mTransientParent->window()->type() == Qt::Popup) ? mTransientParent : nullptr; + if (mSurface) { + { + QWriteLocker lock(&mSurfaceLock); + invalidateSurface(); + if (mTransientParent) + mTransientParent->removeChildPopup(this); + delete mShellSurface; + mShellSurface = nullptr; + emit surfaceRoleDestroyed(); + delete mSubSurfaceWindow; + mSubSurfaceWindow = nullptr; + mTransientParent = nullptr; + mSurface.reset(); + mViewport.reset(); + mFractionalScale.reset(); + } emit wlSurfaceDestroyed(); - QWriteLocker lock(&mSurfaceLock); - mSurface.reset(); } - if (mFrameCallback) { - wl_callback_destroy(mFrameCallback); - mFrameCallback = nullptr; - } + { + QMutexLocker lock(&mFrameSyncMutex); + if (mFrameCallback) { + wl_callback_destroy(mFrameCallback); + mFrameCallback = nullptr; + } - int timerId = mFrameCallbackTimerId.fetchAndStoreOrdered(-1); - if (timerId != -1) { - killTimer(timerId); + mFrameCallbackElapsedTimer.invalidate(); + mWaitingForFrameCallback = false; } - mWaitingForFrameCallback = false; + if (mFrameCallbackCheckIntervalTimerId != -1) { + killTimer(mFrameCallbackCheckIntervalTimerId); + mFrameCallbackCheckIntervalTimerId = -1; + } + mFrameCallbackTimedOut = false; + mWaitingToApplyConfigure = false; + mCanResize = true; + mResizeDirty = false; + mScale = std::nullopt; + mOpaqueArea = QRegion(); mMask = QRegion(); + + mInputRegion = QRegion(); + mTransparentInputRegion = false; + + if (mQueuedBuffer) { + mQueuedBuffer->setBusy(false); + } mQueuedBuffer = nullptr; + mQueuedBufferDamage = QRegion(); + + mDisplay->handleWindowDestroyed(this); } QWaylandWindow *QWaylandWindow::fromWlSurface(::wl_surface *surface) @@ -297,27 +373,34 @@ void QWaylandWindow::setParent(const QPlatformWindow *parent) } } +QString QWaylandWindow::windowTitle() const +{ + return mWindowTitle; +} + void QWaylandWindow::setWindowTitle(const QString &title) { - if (mShellSurface) { - const QString separator = QString::fromUtf8(" \xe2\x80\x94 "); // unicode character U+2014, EM DASH - const QString formatted = formatWindowTitle(title, separator); - - const int libwaylandMaxBufferSize = 4096; - // Some parts of the buffer is used for metadata, so subtract 100 to be on the safe side. - // Also, QString is in utf-16, which means that in the worst case each character will be - // three bytes when converted to utf-8 (which is what libwayland uses), so divide by three. - const int maxLength = libwaylandMaxBufferSize / 3 - 100; - - auto truncated = QStringRef(&formatted).left(maxLength); - if (truncated.length() < formatted.length()) { - qCWarning(lcQpaWayland) << "Window titles longer than" << maxLength << "characters are not supported." - << "Truncating window title (from" << formatted.length() << "chars)"; - } - mShellSurface->setTitle(truncated.toString()); + const QString separator = QString::fromUtf8(" \xe2\x80\x94 "); // unicode character U+2014, EM DASH + const QString formatted = formatWindowTitle(title, separator); + + const int libwaylandMaxBufferSize = 4096; + // Some parts of the buffer is used for metadata, so subtract 100 to be on the safe side. + // Also, QString is in utf-16, which means that in the worst case each character will be + // three bytes when converted to utf-8 (which is what libwayland uses), so divide by three. + const int maxLength = libwaylandMaxBufferSize / 3 - 100; + + auto truncated = QStringView{formatted}.left(maxLength); + if (truncated.size() < formatted.size()) { + qCWarning(lcQpaWayland) << "Window titles longer than" << maxLength << "characters are not supported." + << "Truncating window title (from" << formatted.size() << "chars)"; } - if (mWindowDecoration && window()->isVisible()) + mWindowTitle = truncated.toString(); + + if (mShellSurface) + mShellSurface->setTitle(mWindowTitle); + + if (mWindowDecorationEnabled && window()->isVisible()) mWindowDecoration->update(); } @@ -325,51 +408,131 @@ void QWaylandWindow::setWindowIcon(const QIcon &icon) { mWindowIcon = icon; - if (mWindowDecoration && window()->isVisible()) + if (mWindowDecorationEnabled && window()->isVisible()) mWindowDecoration->update(); } +QRect QWaylandWindow::defaultGeometry() const +{ + return QRect(QPoint(), QSize(500,500)); +} + void QWaylandWindow::setGeometry_helper(const QRect &rect) { - QPlatformWindow::setGeometry(QRect(rect.x(), rect.y(), - qBound(window()->minimumWidth(), rect.width(), window()->maximumWidth()), - qBound(window()->minimumHeight(), rect.height(), window()->maximumHeight()))); + QPlatformWindow::setGeometry(rect); + if (mViewport) + updateViewport(); if (mSubSurfaceWindow) { - QMargins m = QPlatformWindow::parent()->frameMargins(); + QMargins m = static_cast<QWaylandWindow *>(QPlatformWindow::parent())->clientSideMargins(); mSubSurfaceWindow->set_position(rect.x() + m.left(), rect.y() + m.top()); - mSubSurfaceWindow->parent()->window()->requestUpdate(); + + QWaylandWindow *parentWindow = mSubSurfaceWindow->parent(); + if (parentWindow && parentWindow->isExposed()) { + QRect parentExposeGeometry(QPoint(), parentWindow->geometry().size()); + parentWindow->sendExposeEvent(parentExposeGeometry); + } } } -void QWaylandWindow::setGeometry(const QRect &rect) +void QWaylandWindow::setGeometry(const QRect &r) { + auto rect = r; + if (fixedToplevelPositions && !QPlatformWindow::parent() && window()->type() != Qt::Popup + && window()->type() != Qt::ToolTip) { + rect.moveTo(screen()->geometry().topLeft()); + } setGeometry_helper(rect); if (window()->isVisible() && rect.isValid()) { - if (mWindowDecoration) + if (mWindowDecorationEnabled) mWindowDecoration->update(); - if (mResizeAfterSwap && windowType() == Egl && mSentInitialResize) + if (mResizeAfterSwap && windowType() == Egl && mSentInitialResize) { + QMutexLocker lock(&mResizeLock); mResizeDirty = true; - else + } else { QWindowSystemInterface::handleGeometryChange(window(), geometry()); - + } mSentInitialResize = true; } QRect exposeGeometry(QPoint(), geometry().size()); if (isExposed() && !mInResizeFromApplyConfigure && exposeGeometry != mLastExposeGeometry) sendExposeEvent(exposeGeometry); - if (mShellSurface) + if (mShellSurface) { mShellSurface->setWindowGeometry(windowContentGeometry()); + if (!qt_window_private(window())->positionAutomatic && !mInResizeFromApplyConfigure) + mShellSurface->setWindowPosition(windowGeometry().topLeft()); + } + + if (isOpaque() && mMask.isEmpty()) + setOpaqueArea(QRect(QPoint(0, 0), rect.size())); +} + +void QWaylandWindow::updateInputRegion() +{ + if (!mSurface) + return; + + const bool transparentInputRegion = mFlags.testFlag(Qt::WindowTransparentForInput); + + QRegion inputRegion; + if (!transparentInputRegion) + inputRegion = mMask; + + if (mInputRegion == inputRegion && mTransparentInputRegion == transparentInputRegion) + return; + + mInputRegion = inputRegion; + mTransparentInputRegion = transparentInputRegion; + + if (mInputRegion.isEmpty() && !mTransparentInputRegion) { + mSurface->set_input_region(nullptr); + } else { + struct ::wl_region *region = mDisplay->createRegion(mInputRegion); + mSurface->set_input_region(region); + wl_region_destroy(region); + } +} + +void QWaylandWindow::updateViewport() +{ + if (!surfaceSize().isEmpty()) + mViewport->setDestination(surfaceSize()); +} + +void QWaylandWindow::setGeometryFromApplyConfigure(const QPoint &globalPosition, const QSize &sizeWithMargins) +{ + QMargins margins = clientSideMargins(); + + QPoint positionWithoutMargins = globalPosition + QPoint(margins.left(), margins.top()); + int widthWithoutMargins = qMax(sizeWithMargins.width() - (margins.left() + margins.right()), 1); + int heightWithoutMargins = qMax(sizeWithMargins.height() - (margins.top() + margins.bottom()), 1); + + QRect geometry(positionWithoutMargins, QSize(widthWithoutMargins, heightWithoutMargins)); + + mInResizeFromApplyConfigure = true; + setGeometry(geometry); + mInResizeFromApplyConfigure = false; +} + +void QWaylandWindow::repositionFromApplyConfigure(const QPoint &globalPosition) +{ + QMargins margins = clientSideMargins(); + QPoint positionWithoutMargins = globalPosition + QPoint(margins.left(), margins.top()); + + QRect geometry(positionWithoutMargins, windowGeometry().size()); + mInResizeFromApplyConfigure = true; + setGeometry(geometry); + mInResizeFromApplyConfigure = false; } void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset) { - QMargins margins = frameMargins(); - int widthWithoutMargins = qMax(sizeWithMargins.width() - (margins.left()+margins.right()), 1); - int heightWithoutMargins = qMax(sizeWithMargins.height() - (margins.top()+margins.bottom()), 1); + QMargins margins = clientSideMargins(); + int widthWithoutMargins = qMax(sizeWithMargins.width() - (margins.left() + margins.right()), 1); + int heightWithoutMargins = qMax(sizeWithMargins.height() - (margins.top() + margins.bottom()), 1); QRect geometry(windowGeometry().topLeft(), QSize(widthWithoutMargins, heightWithoutMargins)); mOffset += offset; @@ -387,47 +550,35 @@ void QWaylandWindow::sendExposeEvent(const QRect &rect) mLastExposeGeometry = rect; } - -static QVector<QPointer<QWaylandWindow>> activePopups; - -void QWaylandWindow::closePopups(QWaylandWindow *parent) -{ - while (!activePopups.isEmpty()) { - auto popup = activePopups.takeLast(); - if (popup.isNull()) - continue; - if (popup.data() == parent) - return; - popup->reset(); - } -} - -QWaylandScreen *QWaylandWindow::calculateScreenFromSurfaceEvents() const +QPlatformScreen *QWaylandWindow::calculateScreenFromSurfaceEvents() const { + QReadLocker lock(&mSurfaceLock); if (mSurface) { if (auto *screen = mSurface->oldestEnteredScreen()) return screen; } - - return waylandScreen(); + return QPlatformWindow::screen(); } void QWaylandWindow::setVisible(bool visible) { + // Workaround for issue where setVisible may be called with the same value twice + if (lastVisible == visible) + return; + lastVisible = visible; + if (visible) { - if (window()->type() == Qt::Popup || window()->type() == Qt::ToolTip) - activePopups << this; initWindow(); - mDisplay->flushRequests(); - setGeometry(window()->geometry()); + setGeometry(windowGeometry()); // Don't flush the events here, or else the newly visible window may start drawing, but since // there was no frame before it will be stuck at the waitForFrameSync() in // QWaylandShmBackingStore::beginPaint(). + + if (mShellSurface) + mShellSurface->requestActivateOnShow(); } else { sendExposeEvent(QRect()); - if (window()->type() == Qt::Popup) - closePopups(this); reset(); } } @@ -448,23 +599,37 @@ void QWaylandWindow::lower() void QWaylandWindow::setMask(const QRegion &mask) { + QReadLocker locker(&mSurfaceLock); + if (!mSurface) + return; + if (mMask == mask) return; mMask = mask; - if (!mSurface) - return; + updateInputRegion(); - if (mMask.isEmpty()) { - mSurface->set_input_region(nullptr); - } else { - struct ::wl_region *region = mDisplay->createRegion(mMask); - mSurface->set_input_region(region); - wl_region_destroy(region); + if (isOpaque()) { + if (mMask.isEmpty()) + setOpaqueArea(QRect(QPoint(0, 0), geometry().size())); + else + setOpaqueArea(mMask); } +} - mSurface->commit(); +void QWaylandWindow::setAlertState(bool enabled) +{ + if (mShellSurface) + mShellSurface->setAlertState(enabled); +} + +bool QWaylandWindow::isAlertState() const +{ + if (mShellSurface) + return mShellSurface->isAlertState(); + + return false; } void QWaylandWindow::applyConfigureWhenPossible() @@ -481,12 +646,24 @@ void QWaylandWindow::doApplyConfigure() if (!mWaitingToApplyConfigure) return; + Q_ASSERT_X(QThread::currentThreadId() == QThreadData::get2(thread())->threadId.loadRelaxed(), + "QWaylandWindow::doApplyConfigure", "not called from main thread"); + if (mShellSurface) mShellSurface->applyConfigure(); mWaitingToApplyConfigure = false; } +void QWaylandWindow::doApplyConfigureFromOtherThread() +{ + QMutexLocker lock(&mResizeLock); + if (!mCanResize || !mWaitingToApplyConfigure) + return; + doApplyConfigure(); + sendRecursiveExposeEvent(); +} + void QWaylandWindow::setCanResize(bool canResize) { QMutexLocker lock(&mResizeLock); @@ -497,8 +674,13 @@ void QWaylandWindow::setCanResize(bool canResize) QWindowSystemInterface::handleGeometryChange(window(), geometry()); } if (mWaitingToApplyConfigure) { - doApplyConfigure(); - sendExposeEvent(QRect(QPoint(), geometry().size())); + bool inGuiThread = QThread::currentThreadId() == QThreadData::get2(thread())->threadId.loadRelaxed(); + if (inGuiThread) { + doApplyConfigure(); + sendRecursiveExposeEvent(); + } else { + QMetaObject::invokeMethod(this, &QWaylandWindow::doApplyConfigureFromOtherThread, Qt::QueuedConnection); + } } else if (mResizeDirty) { mResizeDirty = false; sendExposeEvent(QRect(QPoint(), geometry().size())); @@ -514,18 +696,39 @@ void QWaylandWindow::applyConfigure() doApplyConfigure(); lock.unlock(); - sendExposeEvent(QRect(QPoint(), geometry().size())); + sendRecursiveExposeEvent(); QWindowSystemInterface::flushWindowSystemEvents(); } +void QWaylandWindow::sendRecursiveExposeEvent() +{ + if (!isExposed()) + sendExposeEvent(QRect()); + else + sendExposeEvent(QRect(QPoint(), geometry().size())); + + for (QWaylandSubSurface *subSurface : std::as_const(mChildren)) { + auto subWindow = subSurface->window(); + subWindow->sendRecursiveExposeEvent(); + } +} + void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y) { - Q_ASSERT(!buffer->committed()); + QReadLocker locker(&mSurfaceLock); + if (mSurface == nullptr) + return; + if (buffer) { + Q_ASSERT(!buffer->committed()); handleUpdate(); - buffer->setBusy(); - - mSurface->attach(buffer->buffer(), x, y); + buffer->setBusy(true); + if (mSurface->version() >= WL_SURFACE_OFFSET_SINCE_VERSION) { + mSurface->offset(x, y); + mSurface->attach(buffer->buffer(), 0, 0); + } else { + mSurface->attach(buffer->buffer(), x, y); + } } else { mSurface->attach(nullptr, 0, 0); } @@ -539,7 +742,20 @@ void QWaylandWindow::attachOffset(QWaylandBuffer *buffer) void QWaylandWindow::damage(const QRect &rect) { - mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height()); + QReadLocker locker(&mSurfaceLock); + if (mSurface == nullptr) + return; + + const qreal s = scale(); + if (mSurface->version() >= 4) { + const QRect bufferRect = + QRectF(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height()) + .toAlignedRect(); + mSurface->damage_buffer(bufferRect.x(), bufferRect.y(), bufferRect.width(), + bufferRect.height()); + } else { + mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height()); + } } void QWaylandWindow::safeCommit(QWaylandBuffer *buffer, const QRegion &damage) @@ -547,21 +763,15 @@ void QWaylandWindow::safeCommit(QWaylandBuffer *buffer, const QRegion &damage) if (isExposed()) { commit(buffer, damage); } else { + if (mQueuedBuffer) { + mQueuedBuffer->setBusy(false); + } mQueuedBuffer = buffer; + mQueuedBuffer->setBusy(true); mQueuedBufferDamage = damage; } } -void QWaylandWindow::handleExpose(const QRegion ®ion) -{ - QWindowSystemInterface::handleExposeEvent(window(), region); - if (mQueuedBuffer) { - commit(mQueuedBuffer, mQueuedBufferDamage); - mQueuedBuffer = nullptr; - mQueuedBufferDamage = QRegion(); - } -} - void QWaylandWindow::commit(QWaylandBuffer *buffer, const QRegion &damage) { Q_ASSERT(isExposed()); @@ -569,12 +779,25 @@ void QWaylandWindow::commit(QWaylandBuffer *buffer, const QRegion &damage) qCDebug(lcWaylandBackingstore) << "Buffer already committed, ignoring."; return; } + + QReadLocker locker(&mSurfaceLock); if (!mSurface) return; attachOffset(buffer); - for (const QRect &rect: damage) - mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height()); + if (mSurface->version() >= 4) { + const qreal s = scale(); + for (const QRect &rect : damage) { + const QRect bufferRect = + QRectF(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height()) + .toAlignedRect(); + mSurface->damage_buffer(bufferRect.x(), bufferRect.y(), bufferRect.width(), + bufferRect.height()); + } + } else { + for (const QRect &rect: damage) + mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height()); + } Q_ASSERT(!buffer->committed()); buffer->setCommitted(); mSurface->commit(); @@ -582,55 +805,61 @@ void QWaylandWindow::commit(QWaylandBuffer *buffer, const QRegion &damage) void QWaylandWindow::commit() { - mSurface->commit(); + QReadLocker locker(&mSurfaceLock); + if (mSurface != nullptr) + mSurface->commit(); } const wl_callback_listener QWaylandWindow::callbackListener = { [](void *data, wl_callback *callback, uint32_t time) { - Q_UNUSED(callback); Q_UNUSED(time); auto *window = static_cast<QWaylandWindow*>(data); - window->handleFrameCallback(); + window->handleFrameCallback(callback); } }; -void QWaylandWindow::handleFrameCallback() +void QWaylandWindow::handleFrameCallback(wl_callback* callback) { - // Stop the timer and stop waiting immediately - int timerId = mFrameCallbackTimerId.fetchAndStoreOrdered(-1); + QMutexLocker locker(&mFrameSyncMutex); + if (!mFrameCallback) { + // This means the callback is already unset by QWaylandWindow::reset. + // The wl_callback object will be destroyed there too. + return; + } + Q_ASSERT(callback == mFrameCallback); + wl_callback_destroy(callback); + mFrameCallback = nullptr; + mWaitingForFrameCallback = false; + mFrameCallbackElapsedTimer.invalidate(); // The rest can wait until we can run it on the correct thread - auto doHandleExpose = [this, timerId]() { - if (timerId != -1) - killTimer(timerId); - - bool wasExposed = isExposed(); - mFrameCallbackTimedOut = false; - if (!wasExposed && isExposed()) // Did setting mFrameCallbackTimedOut make the window exposed? - sendExposeEvent(QRect(QPoint(), geometry().size())); - if (wasExposed && hasPendingUpdateRequest()) - deliverUpdateRequest(); - }; - - if (thread() != QThread::currentThread()) { - QMetaObject::invokeMethod(this, doHandleExpose); - } else { - doHandleExpose(); + if (mWaitingForUpdateDelivery.testAndSetAcquire(false, true)) { + // Queued connection, to make sure we don't call handleUpdate() from inside waitForFrameSync() + // in the single-threaded case. + QMetaObject::invokeMethod(this, &QWaylandWindow::doHandleFrameCallback, Qt::QueuedConnection); } + mFrameSyncWait.notify_all(); } -QMutex QWaylandWindow::mFrameSyncMutex; +void QWaylandWindow::doHandleFrameCallback() +{ + mWaitingForUpdateDelivery.storeRelease(false); + bool wasExposed = isExposed(); + mFrameCallbackTimedOut = false; + if (!wasExposed && isExposed()) // Did setting mFrameCallbackTimedOut make the window exposed? + sendExposeEvent(QRect(QPoint(), geometry().size())); + if (wasExposed && hasPendingUpdateRequest()) + deliverUpdateRequest(); + +} bool QWaylandWindow::waitForFrameSync(int timeout) { - if (!mWaitingForFrameCallback) - return true; - QMutexLocker locker(&mFrameSyncMutex); - wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(mFrameCallback), mFrameQueue); - mDisplay->dispatchQueueWhile(mFrameQueue, [&]() { return mWaitingForFrameCallback; }, timeout); + QDeadlineTimer deadline(timeout); + while (mWaitingForFrameCallback && mFrameSyncWait.wait(&mFrameSyncMutex, deadline)) { } if (mWaitingForFrameCallback) { qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed"; @@ -639,21 +868,29 @@ bool QWaylandWindow::waitForFrameSync(int timeout) sendExposeEvent(QRect()); } - // Stop current frame timer if any, can't use killTimer directly, because we might be on a diffent thread - // Ordered semantics is needed to avoid stopping the timer twice and not miss it when it's - // started by other writes - int fcbId = mFrameCallbackTimerId.fetchAndStoreOrdered(-1); - if (fcbId != -1) - QMetaObject::invokeMethod(this, [this, fcbId] { killTimer(fcbId); }, Qt::QueuedConnection); - return !mWaitingForFrameCallback; } QMargins QWaylandWindow::frameMargins() const { - if (mWindowDecoration) + if (mWindowDecorationEnabled) return mWindowDecoration->margins(); - return QPlatformWindow::frameMargins(); + else if (mShellSurface) + return mShellSurface->serverSideFrameMargins(); + else + return QPlatformWindow::frameMargins(); +} + +QMargins QWaylandWindow::clientSideMargins() const +{ + return mWindowDecorationEnabled ? mWindowDecoration->margins() : QMargins{}; +} + +void QWaylandWindow::setCustomMargins(const QMargins &margins) { + const QMargins oldMargins = mCustomMargins; + mCustomMargins = margins; + propagateSizeHints(); + setGeometry(geometry().marginsRemoved(oldMargins).marginsAdded(margins)); } /*! @@ -661,7 +898,20 @@ QMargins QWaylandWindow::frameMargins() const */ QSize QWaylandWindow::surfaceSize() const { - return geometry().marginsAdded(frameMargins()).size(); + return geometry().marginsAdded(clientSideMargins()).size(); +} + +QMargins QWaylandWindow::windowContentMargins() const +{ + QMargins shadowMargins; + + if (mWindowDecorationEnabled) + shadowMargins = mWindowDecoration->margins(QWaylandAbstractDecoration::ShadowsOnly); + + if (!mCustomMargins.isNull()) + shadowMargins += mCustomMargins; + + return shadowMargins; } /*! @@ -670,11 +920,26 @@ QSize QWaylandWindow::surfaceSize() const */ QRect QWaylandWindow::windowContentGeometry() const { - return QRect(QPoint(), surfaceSize()); + const QMargins margins = windowContentMargins(); + return QRect(QPoint(margins.left(), margins.top()), surfaceSize().shrunkBy(margins)); +} + +/*! + * Converts from wl_surface coordinates to Qt window coordinates. Qt window + * coordinates start inside (not including) the window decorations, while + * wl_surface coordinates start at the first pixel of the buffer. Potentially, + * this should be in the window shadow, although we don't have those. So for + * now, it's the first pixel of the decorations. + */ +QPointF QWaylandWindow::mapFromWlSurface(const QPointF &surfacePosition) const +{ + const QMargins margins = clientSideMargins(); + return QPointF(surfacePosition.x() - margins.left(), surfacePosition.y() - margins.top()); } -wl_surface *QWaylandWindow::wlSurface() +wl_surface *QWaylandWindow::wlSurface() const { + QReadLocker locker(&mSurfaceLock); return mSurface ? mSurface->object() : nullptr; } @@ -683,6 +948,15 @@ QWaylandShellSurface *QWaylandWindow::shellSurface() const return mShellSurface; } +std::any QWaylandWindow::_surfaceRole() const +{ + if (mSubSurfaceWindow) + return mSubSurfaceWindow->object(); + if (mShellSurface) + return mShellSurface->surfaceRole(); + return {}; +} + QWaylandSubSurface *QWaylandWindow::subSurfaceWindow() const { return mSubSurfaceWindow; @@ -690,38 +964,59 @@ QWaylandSubSurface *QWaylandWindow::subSurfaceWindow() const QWaylandScreen *QWaylandWindow::waylandScreen() const { - return static_cast<QWaylandScreen *>(QPlatformWindow::screen()); + auto *platformScreen = QPlatformWindow::screen(); + Q_ASSERT(platformScreen); + if (platformScreen->isPlaceholder()) + return nullptr; + return static_cast<QWaylandScreen *>(platformScreen); } void QWaylandWindow::handleContentOrientationChange(Qt::ScreenOrientation orientation) { - if (mDisplay->compositorVersion() < 2) + mLastReportedContentOrientation = orientation; + updateBufferTransform(); +} + +void QWaylandWindow::updateBufferTransform() +{ + QReadLocker locker(&mSurfaceLock); + if (mSurface == nullptr || mSurface->version() < 2) return; wl_output_transform transform; - bool isPortrait = window()->screen() && window()->screen()->primaryOrientation() == Qt::PortraitOrientation; - switch (orientation) { - case Qt::PrimaryOrientation: - transform = WL_OUTPUT_TRANSFORM_NORMAL; - break; - case Qt::LandscapeOrientation: - transform = isPortrait ? WL_OUTPUT_TRANSFORM_270 : WL_OUTPUT_TRANSFORM_NORMAL; - break; - case Qt::PortraitOrientation: - transform = isPortrait ? WL_OUTPUT_TRANSFORM_NORMAL : WL_OUTPUT_TRANSFORM_90; - break; - case Qt::InvertedLandscapeOrientation: - transform = isPortrait ? WL_OUTPUT_TRANSFORM_90 : WL_OUTPUT_TRANSFORM_180; - break; - case Qt::InvertedPortraitOrientation: - transform = isPortrait ? WL_OUTPUT_TRANSFORM_180 : WL_OUTPUT_TRANSFORM_270; - break; - default: - Q_UNREACHABLE(); + Qt::ScreenOrientation screenOrientation = Qt::PrimaryOrientation; + + if (mSurface->version() >= 6) { + const auto transform = mSurface->preferredBufferTransform().value_or(WL_OUTPUT_TRANSFORM_NORMAL); + if (auto screen = waylandScreen()) + screenOrientation = screen->toScreenOrientation(transform, Qt::PrimaryOrientation); + } else { + if (auto screen = window()->screen()) + screenOrientation = screen->primaryOrientation(); + } + + const bool isPortrait = (screenOrientation == Qt::PortraitOrientation); + + switch (mLastReportedContentOrientation) { + case Qt::PrimaryOrientation: + transform = WL_OUTPUT_TRANSFORM_NORMAL; + break; + case Qt::LandscapeOrientation: + transform = isPortrait ? WL_OUTPUT_TRANSFORM_270 : WL_OUTPUT_TRANSFORM_NORMAL; + break; + case Qt::PortraitOrientation: + transform = isPortrait ? WL_OUTPUT_TRANSFORM_NORMAL : WL_OUTPUT_TRANSFORM_90; + break; + case Qt::InvertedLandscapeOrientation: + transform = isPortrait ? WL_OUTPUT_TRANSFORM_90 : WL_OUTPUT_TRANSFORM_180; + break; + case Qt::InvertedPortraitOrientation: + transform = isPortrait ? WL_OUTPUT_TRANSFORM_180 : WL_OUTPUT_TRANSFORM_270; + break; + default: + Q_UNREACHABLE(); } mSurface->set_buffer_transform(transform); - // set_buffer_transform is double buffered, we need to commit. - mSurface->commit(); } void QWaylandWindow::setOrientationMask(Qt::ScreenOrientations mask) @@ -743,10 +1038,18 @@ void QWaylandWindow::setWindowFlags(Qt::WindowFlags flags) mFlags = flags; createDecoration(); + + QReadLocker locker(&mSurfaceLock); + updateInputRegion(); } bool QWaylandWindow::createDecoration() { + Q_ASSERT_X(QThread::currentThreadId() == QThreadData::get2(thread())->threadId.loadRelaxed(), + "QWaylandWindow::createDecoration", "not called from main thread"); + // TODO: client side decorations do not work with Vulkan backend. + if (window()->surfaceType() == QSurface::VulkanSurface) + return false; if (!mDisplay->supportsWindowDecoration()) return false; @@ -769,12 +1072,17 @@ bool QWaylandWindow::createDecoration() decoration = false; if (mSubSurfaceWindow) decoration = false; - if (mShellSurface && !mShellSurface->wantsDecorations()) + if (!mShellSurface || !mShellSurface->wantsDecorations()) decoration = false; - bool hadDecoration = mWindowDecoration; + bool hadDecoration = mWindowDecorationEnabled; if (decoration && !decorationPluginFailed) { - if (!mWindowDecoration) { + if (!mWindowDecorationEnabled) { + if (mWindowDecoration) { + delete mWindowDecoration; + mWindowDecoration = nullptr; + } + QStringList decorations = QWaylandDecorationFactory::keys(); if (decorations.empty()) { qWarning() << "No decoration plugins available. Running with no decorations."; @@ -792,6 +1100,22 @@ bool QWaylandWindow::createDecoration() } } + if (targetKey.isEmpty()) { + auto unixServices = dynamic_cast<QGenericUnixServices *>( + QGuiApplicationPrivate::platformIntegration()->services()); + const QByteArray currentDesktop = unixServices->desktopEnvironment(); + if (currentDesktop == "GNOME") { + if (decorations.contains("adwaita"_L1)) + targetKey = "adwaita"_L1; + else if (decorations.contains("gnome"_L1)) + targetKey = "gnome"_L1; + } else { + // Do not use Adwaita/GNOME decorations on other DEs + decorations.removeAll("adwaita"_L1); + decorations.removeAll("gnome"_L1); + } + } + if (targetKey.isEmpty()) targetKey = decorations.first(); // first come, first served. @@ -803,19 +1127,29 @@ bool QWaylandWindow::createDecoration() return false; } mWindowDecoration->setWaylandWindow(this); + mWindowDecorationEnabled = true; } } else { - delete mWindowDecoration; - mWindowDecoration = nullptr; + mWindowDecorationEnabled = false; } - if (hadDecoration != (bool)mWindowDecoration) { - for (QWaylandSubSurface *subsurf : qAsConst(mChildren)) { + if (hadDecoration != mWindowDecorationEnabled) { + for (QWaylandSubSurface *subsurf : std::as_const(mChildren)) { QPoint pos = subsurf->window()->geometry().topLeft(); QMargins m = frameMargins(); subsurf->set_position(pos.x() + m.left(), pos.y() + m.top()); } - sendExposeEvent(QRect(QPoint(), geometry().size())); + setGeometry(geometry()); + + // creating a decoration changes our margins which in turn change size hints + propagateSizeHints(); + + // This is a special case where the buffer is recreated, but since + // the content rect remains the same, the widgets remain the same + // size and are not redrawn, leaving the new buffer empty. As a simple + // work-around, we trigger a full extra update whenever the client-side + // window decorations are toggled while the window is showing. + window()->requestUpdate(); } return mWindowDecoration; @@ -823,7 +1157,7 @@ bool QWaylandWindow::createDecoration() QWaylandAbstractDecoration *QWaylandWindow::decoration() const { - return mWindowDecoration; + return mWindowDecorationEnabled ? mWindowDecoration : nullptr; } static QWaylandWindow *closestShellSurfaceWindow(QWindow *window) @@ -839,22 +1173,32 @@ static QWaylandWindow *closestShellSurfaceWindow(QWindow *window) QWaylandWindow *QWaylandWindow::transientParent() const { + return mTransientParent; +} + +QWaylandWindow *QWaylandWindow::guessTransientParent() const +{ // Take the closest window with a shell surface, since the transient parent may be a // QWidgetWindow or some other window without a shell surface, which is then not able to // get mouse events. if (auto transientParent = closestShellSurfaceWindow(window()->transientParent())) return transientParent; - if (QGuiApplication::focusWindow() && (window()->type() == Qt::ToolTip || window()->type() == Qt::Popup)) - return closestShellSurfaceWindow(QGuiApplication::focusWindow()); + if (window()->type() == Qt::Popup) { + if (mTopPopup) + return mTopPopup; + } + + if (window()->type() == Qt::ToolTip || window()->type() == Qt::Popup) + return display()->lastInputWindow(); return nullptr; } void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e) { - if (e.type == QWaylandPointerEvent::Leave) { - if (mWindowDecoration) { + if (e.type == QEvent::Leave) { + if (mWindowDecorationEnabled) { if (mMouseEventsInContentArea) QWindowSystemInterface::handleLeaveEvent(window()); } else { @@ -866,38 +1210,167 @@ void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylan return; } - if (mWindowDecoration) { + if (mWindowDecorationEnabled) { handleMouseEventWithDecoration(inputDevice, e); } else { switch (e.type) { - case QWaylandPointerEvent::Enter: + case QEvent::Enter: QWindowSystemInterface::handleEnterEvent(window(), e.local, e.global); break; - case QWaylandPointerEvent::Press: - case QWaylandPointerEvent::Release: - case QWaylandPointerEvent::Motion: - QWindowSystemInterface::handleMouseEvent(window(), e.timestamp, e.local, e.global, e.buttons, e.modifiers); + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: + QWindowSystemInterface::handleMouseEvent(window(), e.timestamp, e.local, e.global, e.buttons, e.button, e.type, e.modifiers); break; - case QWaylandPointerEvent::Wheel: + case QEvent::Wheel: QWindowSystemInterface::handleWheelEvent(window(), e.timestamp, e.local, e.global, e.pixelDelta, e.angleDelta, e.modifiers, - e.phase, e.source, false); + e.phase, e.source, e.inverted); break; + default: + Q_UNREACHABLE(); } } #if QT_CONFIG(cursor) - if (e.type == QWaylandPointerEvent::Enter) { - QRect contentGeometry = windowContentGeometry().marginsRemoved(frameMargins()); + if (e.type == QEvent::Enter) { + QRect contentGeometry = QRect(QPoint(), surfaceSize()).marginsRemoved(clientSideMargins()); if (contentGeometry.contains(e.local.toPoint())) restoreMouseCursor(inputDevice); } #endif } -bool QWaylandWindow::touchDragDecoration(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, Qt::TouchPointState state, Qt::KeyboardModifiers mods) +#ifndef QT_NO_GESTURES +void QWaylandWindow::handleSwipeGesture(QWaylandInputDevice *inputDevice, + const QWaylandPointerGestureSwipeEvent &e) +{ + switch (e.state) { + case Qt::GestureStarted: + if (mGestureState != GestureNotActive) + qCWarning(lcQpaWaylandInput) << "Unexpected GestureStarted while already active"; + + if (mWindowDecorationEnabled && !mMouseEventsInContentArea) { + // whole gesture sequence will be ignored + mGestureState = GestureActiveInDecoration; + return; + } + + mGestureState = GestureActiveInContentArea; + QWindowSystemInterface::handleGestureEvent(window(), e.timestamp, + inputDevice->mTouchPadDevice, + Qt::BeginNativeGesture, + e.local, e.global, e.fingers); + break; + case Qt::GestureUpdated: + if (mGestureState != GestureActiveInContentArea) + return; + + if (!e.delta.isNull()) { + QWindowSystemInterface::handleGestureEventWithValueAndDelta( + window(), e.timestamp, inputDevice->mTouchPadDevice, + Qt::PanNativeGesture, + 0, e.delta, e.local, e.global, e.fingers); + } + break; + case Qt::GestureFinished: + case Qt::GestureCanceled: + if (mGestureState == GestureActiveInDecoration) { + mGestureState = GestureNotActive; + return; + } + + if (mGestureState != GestureActiveInContentArea) + qCWarning(lcQpaWaylandInput) << "Unexpected" << (e.state == Qt::GestureFinished ? "GestureFinished" : "GestureCanceled"); + + mGestureState = GestureNotActive; + + // There's currently no way to expose cancelled gestures to the rest of Qt, so + // this part of information is lost. + QWindowSystemInterface::handleGestureEvent(window(), e.timestamp, + inputDevice->mTouchPadDevice, + Qt::EndNativeGesture, + e.local, e.global, e.fingers); + break; + default: + break; + } +} + +void QWaylandWindow::handlePinchGesture(QWaylandInputDevice *inputDevice, + const QWaylandPointerGesturePinchEvent &e) { - if (!mWindowDecoration) + switch (e.state) { + case Qt::GestureStarted: + if (mGestureState != GestureNotActive) + qCWarning(lcQpaWaylandInput) << "Unexpected GestureStarted while already active"; + + if (mWindowDecorationEnabled && !mMouseEventsInContentArea) { + // whole gesture sequence will be ignored + mGestureState = GestureActiveInDecoration; + return; + } + + mGestureState = GestureActiveInContentArea; + QWindowSystemInterface::handleGestureEvent(window(), e.timestamp, + inputDevice->mTouchPadDevice, + Qt::BeginNativeGesture, + e.local, e.global, e.fingers); + break; + case Qt::GestureUpdated: + if (mGestureState != GestureActiveInContentArea) + return; + + if (!e.delta.isNull()) { + QWindowSystemInterface::handleGestureEventWithValueAndDelta( + window(), e.timestamp, inputDevice->mTouchPadDevice, + Qt::PanNativeGesture, + 0, e.delta, e.local, e.global, e.fingers); + } + if (e.rotation_delta != 0) { + QWindowSystemInterface::handleGestureEventWithRealValue(window(), e.timestamp, + inputDevice->mTouchPadDevice, + Qt::RotateNativeGesture, + e.rotation_delta, + e.local, e.global, e.fingers); + } + if (e.scale_delta != 0) { + QWindowSystemInterface::handleGestureEventWithRealValue(window(), e.timestamp, + inputDevice->mTouchPadDevice, + Qt::ZoomNativeGesture, + e.scale_delta, + e.local, e.global, e.fingers); + } + break; + case Qt::GestureFinished: + case Qt::GestureCanceled: + if (mGestureState == GestureActiveInDecoration) { + mGestureState = GestureNotActive; + return; + } + + if (mGestureState != GestureActiveInContentArea) + qCWarning(lcQpaWaylandInput) << "Unexpected" << (e.state == Qt::GestureFinished ? "GestureFinished" : "GestureCanceled"); + + mGestureState = GestureNotActive; + + // There's currently no way to expose cancelled gestures to the rest of Qt, so + // this part of information is lost. + QWindowSystemInterface::handleGestureEvent(window(), e.timestamp, + inputDevice->mTouchPadDevice, + Qt::EndNativeGesture, + e.local, e.global, e.fingers); + break; + default: + break; + } +} +#endif // #ifndef QT_NO_GESTURES + + +bool QWaylandWindow::touchDragDecoration(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, QEventPoint::State state, Qt::KeyboardModifiers mods) +{ + if (!mWindowDecorationEnabled) return false; return mWindowDecoration->handleTouch(inputDevice, local, global, state, mods); } @@ -913,16 +1386,14 @@ void QWaylandWindow::handleMouseEventWithDecoration(QWaylandInputDevice *inputDe return; } - QMargins marg = frameMargins(); + QMargins marg = clientSideMargins(); QRect windowRect(0 + marg.left(), 0 + marg.top(), - geometry().size().width() - marg.right(), - geometry().size().height() - marg.bottom()); + geometry().size().width(), + geometry().size().height()); if (windowRect.contains(e.local.toPoint()) || mMousePressedInContentArea != Qt::NoButton) { - QPointF localTranslated = e.local; + const QPointF localTranslated = mapFromWlSurface(e.local); QPointF globalTranslated = e.global; - localTranslated.setX(localTranslated.x() - marg.left()); - localTranslated.setY(localTranslated.y() - marg.top()); globalTranslated.setX(globalTranslated.x() - marg.left()); globalTranslated.setY(globalTranslated.y() - marg.top()); if (!mMouseEventsInContentArea) { @@ -933,21 +1404,23 @@ void QWaylandWindow::handleMouseEventWithDecoration(QWaylandInputDevice *inputDe } switch (e.type) { - case QWaylandPointerEvent::Enter: + case QEvent::Enter: QWindowSystemInterface::handleEnterEvent(window(), localTranslated, globalTranslated); break; - case QWaylandPointerEvent::Press: - case QWaylandPointerEvent::Release: - case QWaylandPointerEvent::Motion: - QWindowSystemInterface::handleMouseEvent(window(), e.timestamp, localTranslated, globalTranslated, e.buttons, e.modifiers); + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: + QWindowSystemInterface::handleMouseEvent(window(), e.timestamp, localTranslated, globalTranslated, e.buttons, e.button, e.type, e.modifiers); break; - case QWaylandPointerEvent::Wheel: { + case QEvent::Wheel: { QWindowSystemInterface::handleWheelEvent(window(), e.timestamp, localTranslated, globalTranslated, e.pixelDelta, e.angleDelta, e.modifiers, - e.phase, e.source, false); + e.phase, e.source, e.inverted); break; } + default: + Q_UNREACHABLE(); } mMouseEventsInContentArea = true; @@ -962,20 +1435,65 @@ void QWaylandWindow::handleMouseEventWithDecoration(QWaylandInputDevice *inputDe void QWaylandWindow::handleScreensChanged() { - QWaylandScreen *newScreen = calculateScreenFromSurfaceEvents(); + QPlatformScreen *newScreen = calculateScreenFromSurfaceEvents(); - if (newScreen == mLastReportedScreen) + if (newScreen->screen() == window()->screen()) return; QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen()); - mLastReportedScreen = newScreen; - int scale = newScreen->scale(); - if (scale != mScale) { - mScale = scale; - if (mSurface && mDisplay->compositorVersion() >= 3) - mSurface->set_buffer_scale(mScale); - ensureSize(); + if (fixedToplevelPositions && !QPlatformWindow::parent() && window()->type() != Qt::Popup + && window()->type() != Qt::ToolTip + && geometry().topLeft() != newScreen->geometry().topLeft()) { + auto geometry = this->geometry(); + geometry.moveTo(newScreen->geometry().topLeft()); + setGeometry(geometry); + } + + updateScale(); + updateBufferTransform(); +} + +void QWaylandWindow::updateScale() +{ + if (mFractionalScale) { + auto preferredScale = mFractionalScale->preferredScale().value_or(1.0); + preferredScale = std::max(1.0, preferredScale); + Q_ASSERT(mViewport); + setScale(preferredScale); + return; + } + + if (mSurface && mSurface->version() >= 6) { + auto preferredScale = mSurface->preferredBufferScale().value_or(1); + preferredScale = std::max(1, preferredScale); + setScale(preferredScale); + return; + } + + int scale = screen()->isPlaceholder() ? 1 : static_cast<QWaylandScreen *>(screen())->scale(); + setScale(scale); +} + +void QWaylandWindow::setScale(qreal newScale) +{ + if (mScale.has_value() && qFuzzyCompare(mScale.value(), newScale)) + return; + mScale = newScale; + + QWindowSystemInterface::handleWindowDevicePixelRatioChanged(window()); + if (mSurface) { + if (mViewport) + updateViewport(); + else if (mSurface->version() >= 3) + mSurface->set_buffer_scale(std::ceil(newScale)); + } + ensureSize(); + + if (isExposed()) { + // redraw at the new DPR + window()->requestUpdate(); + sendExposeEvent(QRect(QPoint(), geometry().size())); } } @@ -988,13 +1506,17 @@ void QWaylandWindow::setMouseCursor(QWaylandInputDevice *device, const QCursor & void QWaylandWindow::restoreMouseCursor(QWaylandInputDevice *device) { - setMouseCursor(device, window()->cursor()); + if (const QCursor *overrideCursor = QGuiApplication::overrideCursor()) + setMouseCursor(device, *overrideCursor); + else + setMouseCursor(device, window()->cursor()); } #endif void QWaylandWindow::requestActivateWindow() { - qCWarning(lcQpaWayland) << "Wayland does not support QWindow::requestActivate()"; + if (mShellSurface) + mShellSurface->requestActivate(); } bool QWaylandWindow::isExposed() const @@ -1019,14 +1541,14 @@ bool QWaylandWindow::isActive() const return mDisplay->isWindowActivated(this); } -int QWaylandWindow::scale() const +qreal QWaylandWindow::scale() const { - return mScale; + return devicePixelRatio(); } qreal QWaylandWindow::devicePixelRatio() const { - return mScale; + return mScale.value_or(waylandScreen() ? waylandScreen()->scale() : 1); } bool QWaylandWindow::setMouseGrabEnabled(bool grab) @@ -1040,10 +1562,28 @@ bool QWaylandWindow::setMouseGrabEnabled(bool grab) return true; } +QWaylandWindow::ToplevelWindowTilingStates QWaylandWindow::toplevelWindowTilingStates() const +{ + return mLastReportedToplevelWindowTilingStates; +} + +void QWaylandWindow::handleToplevelWindowTilingStatesChanged(ToplevelWindowTilingStates states) +{ + mLastReportedToplevelWindowTilingStates = states; +} + +Qt::WindowStates QWaylandWindow::windowStates() const +{ + return mLastReportedWindowStates; +} + void QWaylandWindow::handleWindowStatesChanged(Qt::WindowStates states) { createDecoration(); - QWindowSystemInterface::handleWindowStateChanged(window(), states, mLastReportedWindowStates); + Qt::WindowStates statesWithoutActive = states & ~Qt::WindowActive; + Qt::WindowStates lastStatesWithoutActive = mLastReportedWindowStates & ~Qt::WindowActive; + QWindowSystemInterface::handleWindowStateChanged(window(), statesWithoutActive, + lastStatesWithoutActive); mLastReportedWindowStates = states; } @@ -1080,15 +1620,36 @@ QVariant QWaylandWindow::property(const QString &name, const QVariant &defaultVa return m_properties.value(name, defaultValue); } +#ifdef QT_PLATFORM_WINDOW_HAS_VIRTUAL_SET_BACKING_STORE +void QWaylandWindow::setBackingStore(QPlatformBackingStore *store) +{ + mBackingStore = dynamic_cast<QWaylandShmBackingStore *>(store); +} +#endif + void QWaylandWindow::timerEvent(QTimerEvent *event) { - if (mFrameCallbackTimerId.testAndSetOrdered(event->timerId(), -1)) { - killTimer(event->timerId()); - qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed"; - mFrameCallbackTimedOut = true; - mWaitingForUpdate = false; - sendExposeEvent(QRect()); + if (event->timerId() != mFrameCallbackCheckIntervalTimerId) + return; + + { + QMutexLocker lock(&mFrameSyncMutex); + + bool callbackTimerExpired = mFrameCallbackElapsedTimer.hasExpired(mFrameCallbackTimeout); + if (!mFrameCallbackElapsedTimer.isValid() || callbackTimerExpired ) { + killTimer(mFrameCallbackCheckIntervalTimerId); + mFrameCallbackCheckIntervalTimerId = -1; + } + if (!mFrameCallbackElapsedTimer.isValid() || !callbackTimerExpired) { + return; + } + mFrameCallbackElapsedTimer.invalidate(); } + + qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed"; + mFrameCallbackTimedOut = true; + mWaitingForUpdate = false; + sendExposeEvent(QRect()); } void QWaylandWindow::requestUpdate() @@ -1097,8 +1658,11 @@ void QWaylandWindow::requestUpdate() Q_ASSERT(hasPendingUpdateRequest()); // should be set by QPA // If we have a frame callback all is good and will be taken care of there - if (mWaitingForFrameCallback) - return; + { + QMutexLocker locker(&mFrameSyncMutex); + if (mWaitingForFrameCallback) + return; + } // If we've already called deliverUpdateRequest(), but haven't seen any attach+commit/swap yet // This is a somewhat redundant behavior and might indicate a bug in the calling code, so log @@ -1111,7 +1675,12 @@ void QWaylandWindow::requestUpdate() // so use invokeMethod to delay the delivery a bit. QMetaObject::invokeMethod(this, [this] { // Things might have changed in the meantime - if (hasPendingUpdateRequest() && !mWaitingForFrameCallback) + { + QMutexLocker locker(&mFrameSyncMutex); + if (mWaitingForFrameCallback) + return; + } + if (hasPendingUpdateRequest()) deliverUpdateRequest(); }, Qt::QueuedConnection); } @@ -1122,31 +1691,36 @@ void QWaylandWindow::requestUpdate() void QWaylandWindow::handleUpdate() { qCDebug(lcWaylandBackingstore) << "handleUpdate" << QThread::currentThread(); + // TODO: Should sync subsurfaces avoid requesting frame callbacks? QReadLocker lock(&mSurfaceLock); if (!mSurface) return; - if (mFrameCallback) { - wl_callback_destroy(mFrameCallback); - mFrameCallback = nullptr; - } + QMutexLocker locker(&mFrameSyncMutex); + if (mWaitingForFrameCallback) + return; - mFrameCallback = mSurface->frame(); + struct ::wl_surface *wrappedSurface = reinterpret_cast<struct ::wl_surface *>(wl_proxy_create_wrapper(mSurface->object())); + wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(wrappedSurface), mDisplay->frameEventQueue()); + mFrameCallback = wl_surface_frame(wrappedSurface); + wl_proxy_wrapper_destroy(wrappedSurface); wl_callback_add_listener(mFrameCallback, &QWaylandWindow::callbackListener, this); mWaitingForFrameCallback = true; mWaitingForUpdate = false; - // Stop current frame timer if any, can't use killTimer directly, see comment above. - int fcbId = mFrameCallbackTimerId.fetchAndStoreOrdered(-1); - if (fcbId != -1) - QMetaObject::invokeMethod(this, [this, fcbId] { killTimer(fcbId); }, Qt::QueuedConnection); - // Start a timer for handling the case when the compositor stops sending frame callbacks. - QMetaObject::invokeMethod(this, [this] { // Again; can't do it directly - if (mWaitingForFrameCallback) - mFrameCallbackTimerId = startTimer(100); - }, Qt::QueuedConnection); + if (mFrameCallbackTimeout > 0) { + QMetaObject::invokeMethod(this, [this] { + QMutexLocker locker(&mFrameSyncMutex); + + if (mWaitingForFrameCallback) { + if (mFrameCallbackCheckIntervalTimerId < 0) + mFrameCallbackCheckIntervalTimerId = startTimer(mFrameCallbackTimeout); + mFrameCallbackElapsedTimer.start(); + } + }, Qt::QueuedConnection); + } } void QWaylandWindow::deliverUpdateRequest() @@ -1167,14 +1741,98 @@ void QWaylandWindow::propagateSizeHints() mShellSurface->propagateSizeHints(); } -bool QtWaylandClient::QWaylandWindow::startSystemMove(const QPoint &pos) +bool QWaylandWindow::startSystemResize(Qt::Edges edges) { - Q_UNUSED(pos); - if (auto seat = display()->lastInputDevice()) - return mShellSurface && mShellSurface->move(seat); + if (auto *seat = display()->lastInputDevice()) { + bool rc = mShellSurface && mShellSurface->resize(seat, edges); + seat->handleEndDrag(); + return rc; + } return false; } +bool QtWaylandClient::QWaylandWindow::startSystemMove() +{ + if (auto seat = display()->lastInputDevice()) { + bool rc = mShellSurface && mShellSurface->move(seat); + seat->handleEndDrag(); + return rc; + } + return false; +} + +bool QWaylandWindow::isOpaque() const +{ + return window()->requestedFormat().alphaBufferSize() <= 0; +} + +void QWaylandWindow::setOpaqueArea(const QRegion &opaqueArea) +{ + const QRegion translatedOpaqueArea = opaqueArea.translated(clientSideMargins().left(), clientSideMargins().top()); + + if (translatedOpaqueArea == mOpaqueArea || !mSurface) + return; + + mOpaqueArea = translatedOpaqueArea; + + struct ::wl_region *region = mDisplay->createRegion(translatedOpaqueArea); + mSurface->set_opaque_region(region); + wl_region_destroy(region); +} + +void QWaylandWindow::requestXdgActivationToken(uint serial) +{ + mShellSurface->requestXdgActivationToken(serial); +} + +void QWaylandWindow::setXdgActivationToken(const QString &token) +{ + mShellSurface->setXdgActivationToken(token); +} + +void QWaylandWindow::addChildPopup(QWaylandWindow *child) +{ + if (mShellSurface) + mShellSurface->attachPopup(child->shellSurface()); + mChildPopups.append(child); +} + +void QWaylandWindow::removeChildPopup(QWaylandWindow *child) +{ + if (mShellSurface) + mShellSurface->detachPopup(child->shellSurface()); + mChildPopups.removeAll(child); +} + +void QWaylandWindow::closeChildPopups() { + while (!mChildPopups.isEmpty()) { + auto popup = mChildPopups.takeLast(); + popup->reset(); + } +} + +void QWaylandWindow::reinit() +{ + if (window()->isVisible()) { + initWindow(); + if (hasPendingUpdateRequest()) + deliverUpdateRequest(); + } +} + +bool QWaylandWindow::windowEvent(QEvent *event) +{ + if (event->type() == QEvent::ApplicationPaletteChange + || event->type() == QEvent::ApplicationFontChange) { + if (mWindowDecorationEnabled && window()->isVisible()) + mWindowDecoration->update(); + } + + return QPlatformWindow::windowEvent(event); +} + } QT_END_NAMESPACE + +#include "moc_qwaylandwindow_p.cpp" diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h index c488c2e3f..c1b736c1e 100644 --- a/src/client/qwaylandwindow_p.h +++ b/src/client/qwaylandwindow_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the config.tests of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDWINDOW_H #define QWAYLANDWINDOW_H @@ -56,13 +20,22 @@ #include <QtCore/QReadWriteLock> #include <QtGui/QIcon> +#include <QtGui/QEventPoint> #include <QtCore/QVariant> #include <QtCore/QLoggingCategory> +#include <QtCore/QElapsedTimer> +#include <QtCore/QList> +#include <QtCore/QMap> // for QVariantMap #include <qpa/qplatformwindow.h> +#include <qpa/qplatformwindow_p.h> #include <QtWaylandClient/private/qwayland-wayland.h> +#include <QtWaylandClient/private/qwaylanddisplay_p.h> #include <QtWaylandClient/qtwaylandclientglobal.h> +#include <QtWaylandClient/private/qwaylandshellsurface_p.h> + +#include <QtCore/qpointer.h> struct wl_egl_window; @@ -79,11 +52,17 @@ class QWaylandSubSurface; class QWaylandAbstractDecoration; class QWaylandInputDevice; class QWaylandScreen; +class QWaylandShellIntegration; class QWaylandShmBackingStore; class QWaylandPointerEvent; +class QWaylandPointerGestureSwipeEvent; +class QWaylandPointerGesturePinchEvent; class QWaylandSurface; +class QWaylandFractionalScale; +class QWaylandViewport; -class Q_WAYLAND_CLIENT_EXPORT QWaylandWindow : public QObject, public QPlatformWindow +class Q_WAYLANDCLIENT_EXPORT QWaylandWindow : public QNativeInterface::Private::QWaylandWindow, + public QPlatformWindow { Q_OBJECT public: @@ -93,15 +72,28 @@ public: Vulkan }; - QWaylandWindow(QWindow *window); + enum ToplevelWindowTilingState { + WindowNoState = 0, + WindowTiledLeft = 1, + WindowTiledRight = 2, + WindowTiledTop = 4, + WindowTiledBottom = 8 + }; + Q_DECLARE_FLAGS(ToplevelWindowTilingStates, ToplevelWindowTilingState) + + QWaylandWindow(QWindow *window, QWaylandDisplay *display); ~QWaylandWindow() override; + // Keep Toplevels position on the top left corner of their screen + static inline bool fixedToplevelPositions = true; + virtual WindowType windowType() const = 0; virtual void ensureSize(); WId winId() const override; void setVisible(bool visible) override; void setParent(const QPlatformWindow *parent) override; + QString windowTitle() const; void setWindowTitle(const QString &title) override; inline QIcon windowIcon() const; @@ -109,6 +101,8 @@ public: void setGeometry(const QRect &rect) override; void resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset = {0, 0}); + void repositionFromApplyConfigure(const QPoint &position); + void setGeometryFromApplyConfigure(const QPoint &globalPosition, const QSize &sizeWithMargins); void applyConfigureWhenPossible(); //rename to possible? @@ -119,7 +113,6 @@ public: void damage(const QRect &rect); void safeCommit(QWaylandBuffer *buffer, const QRegion &damage); - void handleExpose(const QRegion ®ion); void commit(QWaylandBuffer *buffer, const QRegion &damage); void commit(); @@ -127,21 +120,35 @@ public: bool waitForFrameSync(int timeout); QMargins frameMargins() const override; + QMargins clientSideMargins() const; + void setCustomMargins(const QMargins &margins) override; QSize surfaceSize() const; + QMargins windowContentMargins() const; QRect windowContentGeometry() const; + QPointF mapFromWlSurface(const QPointF &surfacePosition) const; QWaylandSurface *waylandSurface() const { return mSurface.data(); } - ::wl_surface *wlSurface(); + ::wl_surface *wlSurface() const; + ::wl_surface *surface() const override + { + return wlSurface(); + } static QWaylandWindow *fromWlSurface(::wl_surface *surface); QWaylandDisplay *display() const { return mDisplay; } QWaylandShellSurface *shellSurface() const; + std::any _surfaceRole() const override; QWaylandSubSurface *subSurfaceWindow() const; QWaylandScreen *waylandScreen() const; void handleContentOrientationChange(Qt::ScreenOrientation orientation) override; + void updateBufferTransform(); void setOrientationMask(Qt::ScreenOrientations mask); + ToplevelWindowTilingStates toplevelWindowTilingStates() const; + void handleToplevelWindowTilingStatesChanged(ToplevelWindowTilingStates states); + + Qt::WindowStates windowStates() const; void setWindowState(Qt::WindowStates states) override; void setWindowFlags(Qt::WindowFlags flags) override; void handleWindowStatesChanged(Qt::WindowStates states); @@ -151,7 +158,10 @@ public: void setMask(const QRegion ®ion) override; - int scale() const; + void setAlertState(bool enabled) override; + bool isAlertState() const override; + + qreal scale() const; qreal devicePixelRatio() const override; void requestActivateWindow() override; @@ -161,9 +171,15 @@ public: QWaylandAbstractDecoration *decoration() const; void handleMouse(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e); +#ifndef QT_NO_GESTURES + void handleSwipeGesture(QWaylandInputDevice *inputDevice, + const QWaylandPointerGestureSwipeEvent &e); + void handlePinchGesture(QWaylandInputDevice *inputDevice, + const QWaylandPointerGesturePinchEvent &e); +#endif bool touchDragDecoration(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, - Qt::TouchPointState state, Qt::KeyboardModifiers mods); + QEventPoint::State state, Qt::KeyboardModifiers mods); bool createDecoration(); @@ -174,7 +190,6 @@ public: QWaylandWindow *transientParent() const; - QMutex *resizeMutex() { return &mResizeLock; } void doApplyConfigure(); void setCanResize(bool canResize); @@ -188,99 +203,179 @@ public: QVariant property(const QString &name); QVariant property(const QString &name, const QVariant &defaultValue); +#ifdef QT_PLATFORM_WINDOW_HAS_VIRTUAL_SET_BACKING_STORE + void setBackingStore(QPlatformBackingStore *store) override; +#else void setBackingStore(QWaylandShmBackingStore *backingStore) { mBackingStore = backingStore; } +#endif QWaylandShmBackingStore *backingStore() const { return mBackingStore; } + void setShellIntegration(QWaylandShellIntegration *shellIntegration); + QWaylandShellIntegration *shellIntegration() const { return mShellIntegration; } + bool setKeyboardGrabEnabled(bool) override { return false; } void propagateSizeHints() override; void addAttachOffset(const QPoint point); - bool startSystemMove(const QPoint &pos) override; + bool startSystemResize(Qt::Edges edges) override; + bool startSystemMove() override; void timerEvent(QTimerEvent *event) override; void requestUpdate() override; void handleUpdate(); void deliverUpdateRequest() override; -public slots: + void setXdgActivationToken(const QString &token); + void requestXdgActivationToken(uint serial) override; + + void beginFrame(); + void endFrame(); + + void closeChildPopups(); + void sendRecursiveExposeEvent(); + + virtual void reinit(); + void reset(); + + bool windowEvent(QEvent *event) override; + +public Q_SLOTS: void applyConfigure(); -signals: +Q_SIGNALS: void wlSurfaceCreated(); void wlSurfaceDestroyed(); protected: + virtual void doHandleFrameCallback(); + virtual QRect defaultGeometry() const; + void sendExposeEvent(const QRect &rect); + QWaylandDisplay *mDisplay = nullptr; + + // mSurface can be written by the main thread. Other threads should claim a read lock for access + mutable QReadWriteLock mSurfaceLock; QScopedPointer<QWaylandSurface> mSurface; + QScopedPointer<QWaylandFractionalScale> mFractionalScale; + QScopedPointer<QWaylandViewport> mViewport; + + QWaylandShellIntegration *mShellIntegration = nullptr; QWaylandShellSurface *mShellSurface = nullptr; QWaylandSubSurface *mSubSurfaceWindow = nullptr; - QVector<QWaylandSubSurface *> mChildren; + QList<QWaylandSubSurface *> mChildren; QWaylandAbstractDecoration *mWindowDecoration = nullptr; + bool mWindowDecorationEnabled = false; bool mMouseEventsInContentArea = false; Qt::MouseButtons mMousePressedInContentArea = Qt::NoButton; +#ifndef QT_NO_GESTURES + enum GestureState { + GestureNotActive, + GestureActiveInContentArea, + GestureActiveInDecoration + }; + + // We want gestures started in the decoration area to be completely ignored even if the mouse + // pointer is later moved to content area. Likewise, gestures started in the content area should + // keep sending events even if the mouse pointer is moved over the decoration (consider that + // the events for that gesture will be sent to us even if it's moved outside the window). + // So we track the gesture state and accept or ignore events based on that. Note that + // concurrent gestures of different types are not allowed in the protocol, so single state is + // enough + GestureState mGestureState = GestureNotActive; +#endif + WId mWindowId; - bool mWaitingForFrameCallback = false; bool mFrameCallbackTimedOut = false; // Whether the frame callback has timed out - QAtomicInt mFrameCallbackTimerId = -1; // Started on commit, reset on frame callback - struct ::wl_callback *mFrameCallback = nullptr; - struct ::wl_event_queue *mFrameQueue = nullptr; + int mFrameCallbackCheckIntervalTimerId = -1; + QAtomicInt mWaitingForUpdateDelivery = false; + + bool mWaitingForFrameCallback = false; // Protected by mFrameSyncMutex + QElapsedTimer mFrameCallbackElapsedTimer; // Protected by mFrameSyncMutex + struct ::wl_callback *mFrameCallback = nullptr; // Protected by mFrameSyncMutex + QMutex mFrameSyncMutex; QWaitCondition mFrameSyncWait; // True when we have called deliverRequestUpdate, but the client has not yet attached a new buffer bool mWaitingForUpdate = false; - QMutex mResizeLock; + QRecursiveMutex mResizeLock; bool mWaitingToApplyConfigure = false; bool mCanResize = true; bool mResizeDirty = false; bool mResizeAfterSwap; + int mFrameCallbackTimeout = 100; QVariantMap m_properties; bool mSentInitialResize = false; QPoint mOffset; - int mScale = 1; - QWaylandScreen *mLastReportedScreen = nullptr; + std::optional<qreal> mScale = std::nullopt; + QString mWindowTitle; QIcon mWindowIcon; Qt::WindowFlags mFlags; QRegion mMask; + + // Empty QRegion maps to "infinite" input region, needs a dedicated "deliberately empty" state. + QRegion mInputRegion; + bool mTransparentInputRegion = false; + + QRegion mOpaqueArea; Qt::WindowStates mLastReportedWindowStates = Qt::WindowNoState; + ToplevelWindowTilingStates mLastReportedToplevelWindowTilingStates = WindowNoState; QWaylandShmBackingStore *mBackingStore = nullptr; QWaylandBuffer *mQueuedBuffer = nullptr; QRegion mQueuedBufferDamage; + QMargins mCustomMargins; + + QPointer<QWaylandWindow> mTransientParent; + QList<QPointer<QWaylandWindow>> mChildPopups; + + Qt::ScreenOrientation mLastReportedContentOrientation = Qt::PrimaryOrientation; + +private Q_SLOTS: + void doApplyConfigureFromOtherThread(); + private: void setGeometry_helper(const QRect &rect); void initWindow(); void initializeWlSurface(); bool shouldCreateShellSurface() const; bool shouldCreateSubSurface() const; - void reset(bool sendDestroyEvent = true); - void sendExposeEvent(const QRect &rect); - static void closePopups(QWaylandWindow *parent); - QWaylandScreen *calculateScreenFromSurfaceEvents() const; + QPlatformScreen *calculateScreenFromSurfaceEvents() const; + void setOpaqueArea(const QRegion &opaqueArea); + bool isOpaque() const; + void updateInputRegion(); + void updateViewport(); void handleMouseEventWithDecoration(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e); void handleScreensChanged(); + void updateScale(); + void setScale(qreal newScale); + + QWaylandWindow *guessTransientParent() const; + void addChildPopup(QWaylandWindow *child); + void removeChildPopup(QWaylandWindow *child); bool mInResizeFromApplyConfigure = false; + bool lastVisible = false; QRect mLastExposeGeometry; static const wl_callback_listener callbackListener; - void handleFrameCallback(); + void handleFrameCallback(struct ::wl_callback* callback); - static QMutex mFrameSyncMutex; static QWaylandWindow *mMouseGrab; - - QReadWriteLock mSurfaceLock; + static QWaylandWindow *mTopPopup; friend class QWaylandSubSurface; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QWaylandWindow::ToplevelWindowTilingStates) + inline QIcon QWaylandWindow::windowIcon() const { return mWindowIcon; diff --git a/src/client/qwaylandwindowmanagerintegration.cpp b/src/client/qwaylandwindowmanagerintegration.cpp index dd1acaf72..9668491d2 100644 --- a/src/client/qwaylandwindowmanagerintegration.cpp +++ b/src/client/qwaylandwindowmanagerintegration.cpp @@ -1,46 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandwindowmanagerintegration_p.h" #include "qwaylandscreen_p.h" #include "qwaylandwindow_p.h" #include "qwaylanddisplay_p.h" +#include "qwaylandshellsurface_p.h" #include <stdint.h> #include <QtCore/QEvent> @@ -57,49 +22,25 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { -class QWaylandWindowManagerIntegrationPrivate { -public: - QWaylandWindowManagerIntegrationPrivate(QWaylandDisplay *waylandDisplay); - bool m_blockPropertyUpdates = false; - QWaylandDisplay *m_waylandDisplay = nullptr; - QHash<QWindow*, QVariantMap> m_queuedProperties; - bool m_showIsFullScreen = false; -}; - -QWaylandWindowManagerIntegrationPrivate::QWaylandWindowManagerIntegrationPrivate(QWaylandDisplay *waylandDisplay) - : m_waylandDisplay(waylandDisplay) -{ - -} - -QWaylandWindowManagerIntegration::QWaylandWindowManagerIntegration(QWaylandDisplay *waylandDisplay) - : d_ptr(new QWaylandWindowManagerIntegrationPrivate(waylandDisplay)) +QWaylandWindowManagerIntegration::QWaylandWindowManagerIntegration(QWaylandDisplay *waylandDisplay, + uint id, uint version) + : QtWayland::qt_windowmanager(waylandDisplay->object(), id, version) { - waylandDisplay->addRegistryListener(&wlHandleListenerGlobal, this); } QWaylandWindowManagerIntegration::~QWaylandWindowManagerIntegration() { - + qt_windowmanager_destroy(object()); } bool QWaylandWindowManagerIntegration::showIsFullScreen() const { - Q_D(const QWaylandWindowManagerIntegration); - return d->m_showIsFullScreen; -} - -void QWaylandWindowManagerIntegration::wlHandleListenerGlobal(void *data, wl_registry *registry, uint32_t id, const QString &interface, uint32_t version) -{ - Q_UNUSED(version); - if (interface == QStringLiteral("qt_windowmanager")) - static_cast<QWaylandWindowManagerIntegration *>(data)->init(registry, id, 1); + return m_showIsFullScreen; } void QWaylandWindowManagerIntegration::windowmanager_hints(int32_t showIsFullScreen) { - Q_D(QWaylandWindowManagerIntegration); - d->m_showIsFullScreen = showIsFullScreen; + m_showIsFullScreen = showIsFullScreen; } void QWaylandWindowManagerIntegration::windowmanager_quit() @@ -107,11 +48,9 @@ void QWaylandWindowManagerIntegration::windowmanager_quit() QGuiApplication::quit(); } -void QWaylandWindowManagerIntegration::openUrl_helper(const QUrl &url) +void QWaylandWindowManagerIntegration::openUrl(const QUrl &url) { - Q_ASSERT(isInitialized()); QString data = url.toString(); - static const int chunkSize = 128; while (!data.isEmpty()) { QString chunk = data.left(chunkSize); @@ -123,25 +62,8 @@ void QWaylandWindowManagerIntegration::openUrl_helper(const QUrl &url) open_url(!data.isEmpty(), chunk); } } - -bool QWaylandWindowManagerIntegration::openUrl(const QUrl &url) -{ - if (isInitialized()) { - openUrl_helper(url); - return true; - } - return QGenericUnixServices::openUrl(url); -} - -bool QWaylandWindowManagerIntegration::openDocument(const QUrl &url) -{ - if (isInitialized()) { - openUrl_helper(url); - return true; - } - return QGenericUnixServices::openDocument(url); -} - } QT_END_NAMESPACE + +#include "moc_qwaylandwindowmanagerintegration_p.cpp" diff --git a/src/client/qwaylandwindowmanagerintegration_p.h b/src/client/qwaylandwindowmanagerintegration_p.h index 31de6ddd3..be06d68ee 100644 --- a/src/client/qwaylandwindowmanagerintegration_p.h +++ b/src/client/qwaylandwindowmanagerintegration_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDWINDOWMANAGERINTEGRATION_H #define QWAYLANDWINDOWMANAGERINTEGRATION_H @@ -52,9 +16,6 @@ // #include <QtCore/QObject> -#include <QtCore/QScopedPointer> - -#include <QtServiceSupport/private/qgenericunixservices_p.h> #include <QtWaylandClient/private/qwayland-qt-windowmanager.h> #include <QtWaylandClient/qtwaylandclientglobal.h> @@ -63,34 +24,27 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { -class QWaylandWindow; class QWaylandDisplay; class QWaylandWindowManagerIntegrationPrivate; -class Q_WAYLAND_CLIENT_EXPORT QWaylandWindowManagerIntegration : public QObject, public QGenericUnixServices, public QtWayland::qt_windowmanager +class Q_WAYLANDCLIENT_EXPORT QWaylandWindowManagerIntegration : public QtWayland::qt_windowmanager { - Q_OBJECT - Q_DECLARE_PRIVATE(QWaylandWindowManagerIntegration) + public: - explicit QWaylandWindowManagerIntegration(QWaylandDisplay *waylandDisplay); - ~QWaylandWindowManagerIntegration() override; + explicit QWaylandWindowManagerIntegration(QWaylandDisplay *waylandDisplay, uint id, + uint version); + ~QWaylandWindowManagerIntegration(); - bool openUrl(const QUrl &url) override; - bool openDocument(const QUrl &url) override; + void openUrl(const QUrl &url); bool showIsFullScreen() const; private: - static void wlHandleListenerGlobal(void *data, wl_registry *registry, uint32_t id, - const QString &interface, uint32_t version); - - QScopedPointer<QWaylandWindowManagerIntegrationPrivate> d_ptr; - void windowmanager_hints(int32_t showIsFullScreen) override; void windowmanager_quit() override; - void openUrl_helper(const QUrl &url); + bool m_showIsFullScreen = false; }; QT_END_NAMESPACE diff --git a/src/client/shellintegration/qwaylandclientshellapi_p.h b/src/client/shellintegration/qwaylandclientshellapi_p.h new file mode 100644 index 000000000..984435aa9 --- /dev/null +++ b/src/client/shellintegration/qwaylandclientshellapi_p.h @@ -0,0 +1,34 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWAYLANDCLIENTSHELLAPI_P_H +#define QWAYLANDCLIENTSHELLAPI_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +// N O T E +// ------- +// This file provides a supported API for creating client-side shell +// extensions. Source compatibility will be preserved, but we may break +// forward and backward binary compatibility, even in patch releases. +// +// The supported API contains these classes: +// +// QtWaylandClient::QWaylandShellSurface +// QtWaylandClient::QWaylandShellIntegration +// QtWaylandClient::QWaylandShellIntegrationPlugin + +#include "QtWaylandClient/private/qwaylandshellsurface_p.h" +#include "QtWaylandClient/private/qwaylandshellintegration_p.h" +#include "QtWaylandClient/private/qwaylandshellintegrationplugin_p.h" + +#endif // QWAYLANDCLIENTSHELLAPI_P_H diff --git a/src/client/shellintegration/qwaylandshellintegration.cpp b/src/client/shellintegration/qwaylandshellintegration.cpp new file mode 100644 index 000000000..3d2f21c77 --- /dev/null +++ b/src/client/shellintegration/qwaylandshellintegration.cpp @@ -0,0 +1,18 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#include "qwaylandshellintegration_p.h" +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QtWaylandClient/private/qwaylandwindow_p.h> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +wl_surface *QWaylandShellIntegration::wlSurfaceForWindow(QWaylandWindow *window) +{ + return window->wlSurface(); +} + +} + +QT_END_NAMESPACE diff --git a/src/client/shellintegration/qwaylandshellintegration_p.h b/src/client/shellintegration/qwaylandshellintegration_p.h index ccad00481..791a94043 100644 --- a/src/client/shellintegration/qwaylandshellintegration_p.h +++ b/src/client/shellintegration/qwaylandshellintegration_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Jolla Ltd -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 Jolla Ltd +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDSHELLINTEGRATION_H #define QWAYLANDSHELLINTEGRATION_H @@ -52,43 +16,80 @@ // #include <QtWaylandClient/qtwaylandclientglobal.h> -#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QtWaylandClient/qwaylandclientextension.h> + + + +#include <QDebug> +#include <private/qglobal_p.h> + +struct wl_surface; +struct wl_registry; QT_BEGIN_NAMESPACE +class QWindow; + namespace QtWaylandClient { class QWaylandWindow; class QWaylandDisplay; class QWaylandShellSurface; -class Q_WAYLAND_CLIENT_EXPORT QWaylandShellIntegration +class Q_WAYLANDCLIENT_EXPORT QWaylandShellIntegration { public: QWaylandShellIntegration() {} virtual ~QWaylandShellIntegration() {} - virtual bool initialize(QWaylandDisplay *display) { - m_display = display; - return true; - } + virtual bool initialize(QWaylandDisplay *display) = 0; virtual QWaylandShellSurface *createShellSurface(QWaylandWindow *window) = 0; - virtual void handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) { - if (newFocus) - m_display->handleWindowActivated(newFocus); - if (oldFocus) - m_display->handleWindowDeactivated(oldFocus); - } virtual void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) { Q_UNUSED(resource); Q_UNUSED(window); return nullptr; } -protected: - QWaylandDisplay *m_display = nullptr; + static wl_surface *wlSurfaceForWindow(QWaylandWindow *window); + +}; + +template <typename T> +class Q_WAYLANDCLIENT_EXPORT QWaylandShellIntegrationTemplate : public QWaylandShellIntegration, public QWaylandClientExtension +{ +public: + QWaylandShellIntegrationTemplate(const int ver) : + QWaylandClientExtension(ver) + { + } + + bool initialize(QWaylandDisplay *) override + { + QWaylandClientExtension::initialize(); + return isActive(); + } + + const struct wl_interface *extensionInterface() const override + { + return T::interface(); + } + + void bind(struct ::wl_registry *registry, int id, int ver) override + { + T* instance = static_cast<T *>(this); + // Make sure lowest version is used of the supplied version from the + // developer and the version specified in the protocol and also the + // compositor version. + if (this->version() > T::interface()->version) { + qWarning("Supplied protocol version to QWaylandClientExtensionTemplate is higher than the version of the protocol, using protocol version instead."); + } + int minVersion = qMin(ver, qMin(T::interface()->version, this->version())); + setVersion(minVersion); + instance->init(registry, id, minVersion); + } }; + } QT_END_NAMESPACE diff --git a/src/client/shellintegration/qwaylandshellintegrationfactory.cpp b/src/client/shellintegration/qwaylandshellintegrationfactory.cpp index 48fda14d4..feedb27c5 100644 --- a/src/client/shellintegration/qwaylandshellintegrationfactory.cpp +++ b/src/client/shellintegration/qwaylandshellintegrationfactory.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Jolla Ltd -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 Jolla Ltd +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandshellintegrationfactory_p.h" #include "qwaylandshellintegrationplugin_p.h" @@ -48,54 +12,23 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { -#if QT_CONFIG(library) -Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, qwsifLoader, (QWaylandShellIntegrationFactoryInterface_iid, QLatin1String("/wayland-shell-integration"), Qt::CaseInsensitive)) -Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, directLoader, - (QWaylandShellIntegrationFactoryInterface_iid, QLatin1String(""), Qt::CaseInsensitive)) -#endif -QStringList QWaylandShellIntegrationFactory::keys(const QString &pluginPath) +QStringList QWaylandShellIntegrationFactory::keys() { -#if QT_CONFIG(library) - QStringList list; - if (!pluginPath.isEmpty()) { - QCoreApplication::addLibraryPath(pluginPath); - list = directLoader()->keyMap().values(); - if (!list.isEmpty()) { - const QString postFix = QStringLiteral(" (from ") - + QDir::toNativeSeparators(pluginPath) - + QLatin1Char(')'); - const QStringList::iterator end = list.end(); - for (QStringList::iterator it = list.begin(); it != end; ++it) - (*it).append(postFix); - } - } - list.append(loader()->keyMap().values()); - return list; -#else - return QStringList(); -#endif + return qwsifLoader->keyMap().values(); } -QWaylandShellIntegration *QWaylandShellIntegrationFactory::create(const QString &name, QWaylandDisplay *display, const QStringList &args, const QString &pluginPath) +QWaylandShellIntegration *QWaylandShellIntegrationFactory::create(const QString &name, QWaylandDisplay *display, const QStringList &args) { -#if QT_CONFIG(library) - QScopedPointer<QWaylandShellIntegration> integration; - - // Try loading the plugin from platformPluginPath first: - if (!pluginPath.isEmpty()) { - QCoreApplication::addLibraryPath(pluginPath); - integration.reset(qLoadPlugin<QWaylandShellIntegration, QWaylandShellIntegrationPlugin>(directLoader(), name, args)); - } - if (!integration) - integration.reset(qLoadPlugin<QWaylandShellIntegration, QWaylandShellIntegrationPlugin>(loader(), name, args)); -#endif + std::unique_ptr<QWaylandShellIntegration> integration; + integration.reset(qLoadPlugin<QWaylandShellIntegration, QWaylandShellIntegrationPlugin>(qwsifLoader(), name, args)); if (integration && !integration->initialize(display)) return nullptr; - return integration.take(); + return integration.release(); } } diff --git a/src/client/shellintegration/qwaylandshellintegrationfactory_p.h b/src/client/shellintegration/qwaylandshellintegrationfactory_p.h index 515a18e1f..edc0e70c7 100644 --- a/src/client/shellintegration/qwaylandshellintegrationfactory_p.h +++ b/src/client/shellintegration/qwaylandshellintegrationfactory_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Jolla Ltd -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 Jolla Ltd +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDSHELLINTEGRATIONFACTORY_H #define QWAYLANDSHELLINTEGRATIONFACTORY_H @@ -63,11 +27,11 @@ namespace QtWaylandClient { class QWaylandShellIntegration; -class Q_WAYLAND_CLIENT_EXPORT QWaylandShellIntegrationFactory +class Q_WAYLANDCLIENT_EXPORT QWaylandShellIntegrationFactory { public: - static QStringList keys(const QString &pluginPath = QString()); - static QWaylandShellIntegration *create(const QString &name, QWaylandDisplay *display, const QStringList &args = QStringList(), const QString &pluginPath = QString()); + static QStringList keys(); + static QWaylandShellIntegration *create(const QString &name, QWaylandDisplay *display, const QStringList &args = QStringList()); }; } diff --git a/src/client/shellintegration/qwaylandshellintegrationplugin.cpp b/src/client/shellintegration/qwaylandshellintegrationplugin.cpp index d89e61fb1..0e2338282 100644 --- a/src/client/shellintegration/qwaylandshellintegrationplugin.cpp +++ b/src/client/shellintegration/qwaylandshellintegrationplugin.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Jolla Ltd -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 Jolla Ltd +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylandshellintegrationplugin_p.h" @@ -55,3 +19,5 @@ QWaylandShellIntegrationPlugin::~QWaylandShellIntegrationPlugin() } QT_END_NAMESPACE + +#include "moc_qwaylandshellintegrationplugin_p.cpp" diff --git a/src/client/shellintegration/qwaylandshellintegrationplugin_p.h b/src/client/shellintegration/qwaylandshellintegrationplugin_p.h index d60675690..85339e1e7 100644 --- a/src/client/shellintegration/qwaylandshellintegrationplugin_p.h +++ b/src/client/shellintegration/qwaylandshellintegrationplugin_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Jolla Ltd -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 Jolla Ltd +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWAYLANDSHELLINTEGRATIONPLUGIN_H #define QWAYLANDSHELLINTEGRATIONPLUGIN_H @@ -56,6 +20,7 @@ #include <QtCore/qplugin.h> #include <QtCore/qfactoryinterface.h> #include <QtCore/QObject> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -65,7 +30,7 @@ class QWaylandShellIntegration; #define QWaylandShellIntegrationFactoryInterface_iid "org.qt-project.Qt.WaylandClient.QWaylandShellIntegrationFactoryInterface.5.3" -class Q_WAYLAND_CLIENT_EXPORT QWaylandShellIntegrationPlugin : public QObject +class Q_WAYLANDCLIENT_EXPORT QWaylandShellIntegrationPlugin : public QObject { Q_OBJECT public: |