From 0f5e719b61abc8af120fe64d8297d111110075cd Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Thu, 26 Oct 2023 17:03:27 +0300 Subject: [PATCH] wayland: Fix none keyboard interactivity in LayerShellV1Window Window::acceptsFocus() is not taken into account when a window is activated using Workspace::activateWindow(). The main reason is because of different input models on X11. Instead, Window::takeFocus() should check itself if the window accepts focus. --- autotests/integration/CMakeLists.txt | 2 +- .../integration/layershellv1window_test.cpp | 60 +++- .../protocols/wlr-layer-shell-unstable-v1.xml | 325 ------------------ .../integration/showing_desktop_test.cpp | 1 + src/layershellv1window.cpp | 4 +- 5 files changed, 59 insertions(+), 333 deletions(-) delete mode 100644 autotests/integration/protocols/wlr-layer-shell-unstable-v1.xml diff --git a/autotests/integration/CMakeLists.txt b/autotests/integration/CMakeLists.txt index 2314260941..14badaa5ae 100644 --- a/autotests/integration/CMakeLists.txt +++ b/autotests/integration/CMakeLists.txt @@ -10,7 +10,7 @@ qt6_generate_wayland_protocol_client_sources(KWinIntegrationTestFramework qt6_generate_wayland_protocol_client_sources(KWinIntegrationTestFramework FILES ${WaylandProtocols_DATADIR}/unstable/text-input/text-input-unstable-v3.xml - ${CMAKE_CURRENT_SOURCE_DIR}/protocols/wlr-layer-shell-unstable-v1.xml + ${CMAKE_SOURCE_DIR}/src/wayland/protocols/wlr-layer-shell-unstable-v1.xml ${WaylandProtocols_DATADIR}/stable/xdg-shell/xdg-shell.xml ${WaylandProtocols_DATADIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml ${WaylandProtocols_DATADIR}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml diff --git a/autotests/integration/layershellv1window_test.cpp b/autotests/integration/layershellv1window_test.cpp index 4aaf86ae40..ffa55de5a1 100644 --- a/autotests/integration/layershellv1window_test.cpp +++ b/autotests/integration/layershellv1window_test.cpp @@ -47,7 +47,8 @@ private Q_SLOTS: void testFill_data(); void testFill(); void testStack(); - void testFocus(); + void testKeyboardInteractivityNone(); + void testKeyboardInteractivityOnDemand(); void testActivate_data(); void testActivate(); void testUnmap(); @@ -508,15 +509,15 @@ void LayerShellV1WindowTest::testStack() QVERIFY(Test::waitForWindowClosed(window2)); } -void LayerShellV1WindowTest::testFocus() +void LayerShellV1WindowTest::testKeyboardInteractivityNone() { // Create a layer shell surface. std::unique_ptr surface(Test::createSurface()); std::unique_ptr shellSurface(Test::createLayerSurfaceV1(surface.get(), QStringLiteral("test"))); // Set the initial state of the layer surface. - shellSurface->set_keyboard_interactivity(1); - shellSurface->set_size(280, 124); + shellSurface->set_keyboard_interactivity(0); + shellSurface->set_size(100, 50); surface->commit(KWayland::Client::Surface::CommitFlag::None); // Wait for the compositor to position the surface. @@ -528,15 +529,62 @@ void LayerShellV1WindowTest::testFocus() shellSurface->ack_configure(configureRequestedSpy.last().at(0).toUInt()); Window *window = Test::renderAndWaitForShown(surface.get(), requestedSize, Qt::red); QVERIFY(window); + QVERIFY(!window->isActive()); - // The layer surface must be focused when it's mapped. - QVERIFY(window->isActive()); + // Try to activate the surface. + workspace()->activateWindow(window); + QVERIFY(!window->isActive()); // Destroy the window. shellSurface.reset(); QVERIFY(Test::waitForWindowClosed(window)); } +void LayerShellV1WindowTest::testKeyboardInteractivityOnDemand() +{ + // Create a layer shell surface. + std::unique_ptr surface1(Test::createSurface()); + std::unique_ptr shellSurface1(Test::createLayerSurfaceV1(surface1.get(), QStringLiteral("test"))); + shellSurface1->set_keyboard_interactivity(1); + shellSurface1->set_size(280, 124); + surface1->commit(KWayland::Client::Surface::CommitFlag::None); + + QSignalSpy configureRequestedSpy1(shellSurface1.get(), &Test::LayerSurfaceV1::configureRequested); + QVERIFY(configureRequestedSpy1.wait()); + const QSize requestedSize1 = configureRequestedSpy1.last().at(1).toSize(); + shellSurface1->ack_configure(configureRequestedSpy1.last().at(0).toUInt()); + Window *window1 = Test::renderAndWaitForShown(surface1.get(), requestedSize1, Qt::red); + QVERIFY(window1); + QVERIFY(window1->isActive()); + + // Create the second layer shell surface. + std::unique_ptr surface2(Test::createSurface()); + std::unique_ptr shellSurface2(Test::createLayerSurfaceV1(surface2.get(), QStringLiteral("test"))); + shellSurface2->set_keyboard_interactivity(1); + shellSurface2->set_size(280, 124); + surface2->commit(KWayland::Client::Surface::CommitFlag::None); + + QSignalSpy configureRequestedSpy2(shellSurface2.get(), &Test::LayerSurfaceV1::configureRequested); + QVERIFY(configureRequestedSpy2.wait()); + const QSize requestedSize2 = configureRequestedSpy2.last().at(1).toSize(); + shellSurface2->ack_configure(configureRequestedSpy2.last().at(0).toUInt()); + Window *window2 = Test::renderAndWaitForShown(surface2.get(), requestedSize2, Qt::red); + QVERIFY(window2); + QVERIFY(window2->isActive()); + QVERIFY(!window1->isActive()); + + // Activate the first surface. + workspace()->activateWindow(window1); + QVERIFY(window1->isActive()); + QVERIFY(!window2->isActive()); + + // Destroy the window. + shellSurface1.reset(); + QVERIFY(Test::waitForWindowClosed(window1)); + shellSurface2.reset(); + QVERIFY(Test::waitForWindowClosed(window2)); +} + void LayerShellV1WindowTest::testActivate_data() { QTest::addColumn("layer"); diff --git a/autotests/integration/protocols/wlr-layer-shell-unstable-v1.xml b/autotests/integration/protocols/wlr-layer-shell-unstable-v1.xml deleted file mode 100644 index 0736a45e26..0000000000 --- a/autotests/integration/protocols/wlr-layer-shell-unstable-v1.xml +++ /dev/null @@ -1,325 +0,0 @@ - - - - Copyright © 2017 Drew DeVault - - Permission to use, copy, modify, distribute, and sell this - software and its documentation for any purpose is hereby granted - without fee, provided that the above copyright notice appear in - all copies and that both that copyright notice and this permission - notice appear in supporting documentation, and that the name of - the copyright holders not be used in advertising or publicity - pertaining to distribution of the software without specific, - written prior permission. The copyright holders make no - representations about the suitability of this software for any - purpose. It is provided "as is" without express or implied - warranty. - - THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN - AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF - THIS SOFTWARE. - - - - - Clients can use this interface to assign the surface_layer role to - wl_surfaces. Such surfaces are assigned to a "layer" of the output and - rendered with a defined z-depth respective to each other. They may also be - anchored to the edges and corners of a screen and specify input handling - semantics. This interface should be suitable for the implementation of - many desktop shell components, and a broad number of other applications - that interact with the desktop. - - - - - Create a layer surface for an existing surface. This assigns the role of - layer_surface, or raises a protocol error if another role is already - assigned. - - Creating a layer surface from a wl_surface which has a buffer attached - or committed is a client error, and any attempts by a client to attach - or manipulate a buffer prior to the first layer_surface.configure call - must also be treated as errors. - - After creating a layer_surface object and setting it up, the client - must perform an initial commit without any buffer attached. - The compositor will reply with a layer_surface.configure event. - The client must acknowledge it and is then allowed to attach a buffer - to map the surface. - - You may pass NULL for output to allow the compositor to decide which - output to use. Generally this will be the one that the user most - recently interacted with. - - Clients can specify a namespace that defines the purpose of the layer - surface. - - - - - - - - - - - - - - - - - These values indicate which layers a surface can be rendered in. They - are ordered by z depth, bottom-most first. Traditional shell surfaces - will typically be rendered between the bottom and top layers. - Fullscreen shell surfaces are typically rendered at the top layer. - Multiple surfaces can share a single layer, and ordering within a - single layer is undefined. - - - - - - - - - - - - - This request indicates that the client will not use the layer_shell - object any more. Objects that have been created through this instance - are not affected. - - - - - - - An interface that may be implemented by a wl_surface, for surfaces that - are designed to be rendered as a layer of a stacked desktop-like - environment. - - Layer surface state (layer, size, anchor, exclusive zone, - margin, interactivity) is double-buffered, and will be applied at the - time wl_surface.commit of the corresponding wl_surface is called. - - Attaching a null buffer to a layer surface unmaps it. - - Unmapping a layer_surface means that the surface cannot be shown by the - compositor until it is explicitly mapped again. The layer_surface - returns to the state it had right after layer_shell.get_layer_surface. - The client can re-map the surface by performing a commit without any - buffer attached, waiting for a configure event and handling it as usual. - - - - - Sets the size of the surface in surface-local coordinates. The - compositor will display the surface centered with respect to its - anchors. - - If you pass 0 for either value, the compositor will assign it and - inform you of the assignment in the configure event. You must set your - anchor to opposite edges in the dimensions you omit; not doing so is a - protocol error. Both values are 0 by default. - - Size is double-buffered, see wl_surface.commit. - - - - - - - - Requests that the compositor anchor the surface to the specified edges - and corners. If two orthogonal edges are specified (e.g. 'top' and - 'left'), then the anchor point will be the intersection of the edges - (e.g. the top left corner of the output); otherwise the anchor point - will be centered on that edge, or in the center if none is specified. - - Anchor is double-buffered, see wl_surface.commit. - - - - - - - Requests that the compositor avoids occluding an area with other - surfaces. The compositor's use of this information is - implementation-dependent - do not assume that this region will not - actually be occluded. - - A positive value is only meaningful if the surface is anchored to one - edge or an edge and both perpendicular edges. If the surface is not - anchored, anchored to only two perpendicular edges (a corner), anchored - to only two parallel edges or anchored to all edges, a positive value - will be treated the same as zero. - - A positive zone is the distance from the edge in surface-local - coordinates to consider exclusive. - - Surfaces that do not wish to have an exclusive zone may instead specify - how they should interact with surfaces that do. If set to zero, the - surface indicates that it would like to be moved to avoid occluding - surfaces with a positive exclusive zone. If set to -1, the surface - indicates that it would not like to be moved to accommodate for other - surfaces, and the compositor should extend it all the way to the edges - it is anchored to. - - For example, a panel might set its exclusive zone to 10, so that - maximized shell surfaces are not shown on top of it. A notification - might set its exclusive zone to 0, so that it is moved to avoid - occluding the panel, but shell surfaces are shown underneath it. A - wallpaper or lock screen might set their exclusive zone to -1, so that - they stretch below or over the panel. - - The default value is 0. - - Exclusive zone is double-buffered, see wl_surface.commit. - - - - - - - Requests that the surface be placed some distance away from the anchor - point on the output, in surface-local coordinates. Setting this value - for edges you are not anchored to has no effect. - - The exclusive zone includes the margin. - - Margin is double-buffered, see wl_surface.commit. - - - - - - - - - - Set to 1 to request that the seat send keyboard events to this layer - surface. For layers below the shell surface layer, the seat will use - normal focus semantics. For layers above the shell surface layers, the - seat will always give exclusive keyboard focus to the top-most layer - which has keyboard interactivity set to true. - - Layer surfaces receive pointer, touch, and tablet events normally. If - you do not want to receive them, set the input region on your surface - to an empty region. - - Events is double-buffered, see wl_surface.commit. - - - - - - - This assigns an xdg_popup's parent to this layer_surface. This popup - should have been created via xdg_surface::get_popup with the parent set - to NULL, and this request must be invoked before committing the popup's - initial state. - - See the documentation of xdg_popup for more details about what an - xdg_popup is and how it is used. - - - - - - - When a configure event is received, if a client commits the - surface in response to the configure event, then the client - must make an ack_configure request sometime before the commit - request, passing along the serial of the configure event. - - If the client receives multiple configure events before it - can respond to one, it only has to ack the last configure event. - - A client is not required to commit immediately after sending - an ack_configure request - it may even ack_configure several times - before its next surface commit. - - A client may send multiple ack_configure requests before committing, but - only the last request sent before a commit indicates which configure - event the client really is responding to. - - - - - - - This request destroys the layer surface. - - - - - - The configure event asks the client to resize its surface. - - Clients should arrange their surface for the new states, and then send - an ack_configure request with the serial sent in this configure event at - some point before committing the new surface. - - The client is free to dismiss all but the last configure event it - received. - - The width and height arguments specify the size of the window in - surface-local coordinates. - - The size is a hint, in the sense that the client is free to ignore it if - it doesn't resize, pick a smaller size (to satisfy aspect ratio or - resize in steps of NxM pixels). If the client picks a smaller size and - is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the - surface will be centered on this axis. - - If the width or height arguments are zero, it means the client should - decide its own window dimension. - - - - - - - - - The closed event is sent by the compositor when the surface will no - longer be shown. The output may have been destroyed or the user may - have asked for it to be removed. Further changes to the surface will be - ignored. The client should destroy the resource after receiving this - event, and create a new surface if they so choose. - - - - - - - - - - - - - - - - - - - - - Change the layer that the surface is rendered on. - - Layer is double-buffered, see wl_surface.commit. - - - - - diff --git a/autotests/integration/showing_desktop_test.cpp b/autotests/integration/showing_desktop_test.cpp index 88ac7c6be9..4ab61d2590 100644 --- a/autotests/integration/showing_desktop_test.cpp +++ b/autotests/integration/showing_desktop_test.cpp @@ -80,6 +80,7 @@ void ShowingDesktopTest::testRestoreFocusWithDesktopWindow() std::unique_ptr desktopSurface(Test::createSurface()); std::unique_ptr desktopShellSurface(Test::createLayerSurfaceV1(desktopSurface.get(), QStringLiteral("desktop"))); + desktopShellSurface->set_keyboard_interactivity(1); desktopShellSurface->set_layer(Test::LayerShellV1::layer_background); desktopShellSurface->set_size(0, 0); desktopShellSurface->set_exclusive_zone(-1); diff --git a/src/layershellv1window.cpp b/src/layershellv1window.cpp index 3f05b2c580..352363a1d9 100644 --- a/src/layershellv1window.cpp +++ b/src/layershellv1window.cpp @@ -125,7 +125,9 @@ bool LayerShellV1Window::isResizable() const bool LayerShellV1Window::takeFocus() { - setActive(true); + if (acceptsFocus()) { + setActive(true); + } return true; }