Compare commits

..

395 Commits

Author SHA1 Message Date
Vlad Zahorodnii 166988cef7 plugins/screencast: Handle failing to import dmabuf
(cherry picked from commit 5c2314a6bd)
2024-03-27 15:54:20 +00:00
Vlad Zahorodnii 09e638ff55 plugins/screencast: Simplify damage calculation in region screen cast source
Whether the output is rotated should be irrelevant. The region screen
cast source handles scrapping fairly well blitting from rotated outputs.

(cherry picked from commit 8d3c06a178)
2024-03-27 10:44:38 +00:00
Vlad Zahorodnii 45fbfb7222 plugins/screencast: Drop "stream" in ScreenCastStream::streamReady
The word "stream" is redundant.

(cherry picked from commit d1fdb69946)
2024-03-27 10:44:38 +00:00
Vlad Zahorodnii 6e9f1f3eeb plugins/screencast: Rename ScreenCastStream::stop() to close()
This is to help disambiguating between the code that shuts down the
stream completely and one that pauses/resumes the stream.

(cherry picked from commit cd2a4de82f)
2024-03-27 10:44:38 +00:00
Vlad Zahorodnii f4230e4a09 plugins/screencast: Pause/resume source when stream is paused/resumed
(cherry picked from commit 7894db7504)
2024-03-27 10:44:38 +00:00
Niccolò Venerandi ee0dd6c8c5 Fix oversights on shortcut handling within Overview/Grid effect
BUG:482931


(cherry picked from commit c8e0568022)
2024-03-27 10:26:42 +00:00
l10n daemon script c9b94bbac0 GIT_SILENT Sync po/docbooks with svn 2024-03-27 03:27:50 +00:00
Vlad Zahorodnii 13e31baca8 tiles: Evacuate windows in CustomTile::remove()
(cherry picked from commit 9d3e87bd9d)
2024-03-27 01:33:41 +02:00
Vlad Zahorodnii 74060343a0 tiles: Use deleteLater() in CustomTile::remove()
Otherwise it seems that QQmlEngine can segfault.

(cherry picked from commit ea9691a2f5)
2024-03-27 01:33:35 +02:00
Jonathan Riddell 501ab30677 update version for new release 2024-03-26 14:23:27 +00:00
l10n daemon script f38fd572db GIT_SILENT Sync po/docbooks with svn 2024-03-26 03:30:10 +00:00
Vlad Zahorodnii 551100b469 plugins/screencast: Neutralize stopped streams
Streams are deleted with QObject::deleteLater(), it's still possible to
process some events before the stream is actually destroyed, which can
cause problems.


(cherry picked from commit 8428b4603c)
2024-03-25 20:24:41 +00:00
Xaver Hugl a1d0105836 backends/wayland: don't clear the format map on tranche_done
It's not per tranche, it's global. Clearing the map means we ignore formats
from non-preferred tranches entirely


(cherry picked from commit c6c99c1740)
2024-03-25 13:46:47 +00:00
l10n daemon script 91d0849250 GIT_SILENT Sync po/docbooks with svn 2024-03-25 03:24:42 +00:00
David Edmundson 760e35065e xwayland: Allow pushing to the clipboard without focus
X11 did not have a requirement that apps needed keyboard focus to update
a clipboard. Apps could copy things on click. With context menus and
grabs there can be no active window at this point.

Kwin tried to retrofit a requirement, which doesn't work in all cases.

Whilst there are security implications of reading a clipboard there are
no security issues about pushing a new clipboard. Gnome also allows X11
apps to push to the clipboard at any point.


(cherry picked from commit b2a48d09e7)
2024-03-22 14:17:39 +00:00
Nicolas Fella d8019dbf16 kcms/decoration: Fix crash when preview cannot be generated
m_bridge->createDecoration() can return nullptr. Most codepaths
already guard against that, but not all of them

BUG: 456531
(cherry picked from commit 0fef229587)
2024-03-22 13:58:48 +01:00
Vlad Zahorodnii f2a7876178 plugins/screencast: Prefer allocating 3 buffers per stream by default
The current default 16 is way too many. For example, when screencasting
a 4K output, about 500MB will be wasted.


(cherry picked from commit 831ca28f93)
2024-03-22 09:35:27 +00:00
l10n daemon script 9a029a423e GIT_SILENT Sync po/docbooks with svn 2024-03-22 02:55:08 +00:00
Vlad Zahorodnii ac831f0b97 plugins/screenshot: Fix a crash in ScreenShotSource2::marshal()
There are behavior differences between QFutureInterface and QPromise.
The QFutureWatcher::finished() signal will be emitted in both cases
when the promise reports results or it is canceled.


(cherry picked from commit 8bf1f9203b)
2024-03-21 14:16:48 +00:00
Vlad Zahorodnii 79fa595d25 plugins/screencast: Enqueue buffers immediately
There's still a crash in pw_stream_queue_buffer().

(cherry picked from commit a7e7923d6b)
2024-03-21 12:43:14 +00:00
Vlad Zahorodnii 2d0117de92 Disable placeholder output when removing it
The current assumption is that Output::enabledChanged(false) always comes
before Output::destroyed.


(cherry picked from commit 489cd31ee9)
2024-03-21 12:24:30 +00:00
l10n daemon script 3bb54d629f GIT_SILENT Sync po/docbooks with svn 2024-03-21 03:01:58 +00:00
Yifan Zhu 5af86d18f4 x11window: round border size to integral XNative units
Both frameSize and clientSize are rounded to integral XNative units.
So their difference must also be rounded to integral XNative units.
Otherwise we get cycles of rounding that can cause growing window sizes.

BUG: 481460


(cherry picked from commit 111657ad04)
2024-03-20 21:37:50 +00:00
Vlad Zahorodnii 045199f3a4 backends/drm: Guard against null m_frame in DrmAbstractOutput::{frameFailed,pageFlipped} 2024-03-20 15:44:48 +00:00
Vlad Zahorodnii e378a35d05 backends/drm: Reset m_frame when present fails 2024-03-20 15:44:48 +00:00
Vlad Zahorodnii ea8fa09e2d Fix warning about nullptr sender in QObject::connect()
When one sets "no border" property, the decoration can be null.


(cherry picked from commit 012c64b054)
2024-03-20 14:54:22 +00:00
Vlad Zahorodnii 0dbbcd83fe backends/drm: Handle failing to reopen drm node
Otherwise the drm backend will crash.


(cherry picked from commit 236baa63df)
2024-03-20 14:17:10 +00:00
Vlad Zahorodnii e2aec38015 Add more guards for closed windows
(cherry picked from commit 8934a31dad)
2024-03-20 14:06:58 +00:00
Vlad Zahorodnii c1c7de07aa wayland: Remove zombie ClientConnection from Display later
Otherwise it's theoretically possible to create a new ClientConnection
object for the zombie wl_client when its resources are being destroyed.
For example

- process early wl_client destroy notification
- the ClientConnection objects gets removed from the client list in Display
- process wl_resource objects getting destroyed
- if some code calls display->getConnection(zombie_client), it's going to
  reintroduce the client in the client list
- process late wl_client destroy notification, it's going to destroy the
  original and the clone ClientConnection object

This change prevents reintoducing a clone client object, by keeping the
original for a bit longer until it's actually destroyed. In the future though,
it would be great to kill the client lists in Display and ClientConnection,
and just use `static_cast<ClientConnection *>(wl_client_get_user_data())`.


(cherry picked from commit 442648edc0)
2024-03-20 13:52:14 +00:00
Vlad Zahorodnii d38a8d48a9 Don't trigger screen edge if the pointer is constrained
If the pointer is constrained, the screen edge may push the cursor back,
which will work not as expected. It's also undesired to trigger the screen
edge while the pointer is constrained by some surface, in general.


(cherry picked from commit 44b72823c2)
2024-03-20 13:37:02 +00:00
l10n daemon script 37ae820b29 GIT_SILENT Sync po/docbooks with svn 2024-03-20 02:59:09 +00:00
Xaver Hugl 0b2d8901ab pointer input: handle warp events differently from absolute motion events
As Wayland doesn't have a warp event yet, before this commit, warps were
dealt with like normal absolute motion events. This trips up games though,
which don't deal with actual absolute motion events well. As a solution
to that, until an actual warp event is a thing, we send a motion event with
a position + a relative motion event with no motion

BUG: 458233
CCBUG: 482476
(cherry picked from commit 630ba5fab4)
2024-03-19 20:31:58 +01:00
Vlad Zahorodnii fe1c814ee1 plugins/screencast: Improve code readability
(cherry picked from commit 3ebe34bc3f)
2024-03-19 14:38:30 +02:00
Vlad Zahorodnii bb1507eadf plugins/screencast: Guard against having no dmabuf data for particular buffer
(cherry picked from commit 75f94c7cd0)
2024-03-19 14:38:23 +02:00
Vlad Zahorodnii 5521920d31 plugins/screencast: Properly mark pw buffers as corrupted
(cherry picked from commit afbb878fca)
2024-03-19 14:38:15 +02:00
Vlad Zahorodnii 6000cca756 plugins/screencast: Clean up how pw_buffer is initialized
The offset and the stride of dmabuf can be set during the pw_buffer
initialization.

(cherry picked from commit 01fa6c0af0)
2024-03-19 14:38:08 +02:00
Vlad Zahorodnii 5164f6969e plugins/kpackage: Fix mainscript for declarative effects
(cherry picked from commit 7bfb4a93e2)
2024-03-19 11:40:57 +00:00
Yifan Zhu 90e070d004 window: fix interactiveMove exit condition
Previously the <= 1.0 test always succeeds in the first try, causing the
loop to exit prematurely.

BUG:  481610
(cherry picked from commit 701f914081)
2024-03-18 20:44:56 -07:00
l10n daemon script abeec73a47 GIT_SILENT Sync po/docbooks with svn 2024-03-19 03:02:51 +00:00
Vlad Zahorodnii 46d6ad9e94 plugins/screencast: Add missing Q_OBJECT
(cherry picked from commit 29135f188f)
2024-03-18 17:14:53 +00:00
Vlad Zahorodnii bd681c3c72 Fix the titlebar visibility check for small windows
Consider a window with the size of 50x100 and the titlebar height of 36px.
The maximum number of visible titlebar pixels is 1800, but the
titleBarRect() function reports 3600 instead, which is completely wrong.

Since the reported number of required visible pixels is wrong, the window
geometry constraining logic is mistriggered and it's possible to move the
window only by 1px.
2024-03-18 15:17:45 +00:00
Vlad Zahorodnii 808ff015ad plugins/screencast: Avoid closing dmabuf fds twice
dmabuf fds are owned by the GraphicsBuffer, which will be destroyed when
`m_dmabufDataForPwBuffer.remove(buffer);` is executed.


(cherry picked from commit fb7dd07076)
2024-03-18 14:52:40 +00:00
David Edmundson 745625f04d effects: Do not take ownership of QuickEffect::delegate
QuickEffect::setDelegate is exposed QML API.

The lifespan of assigned objects is therefore managed by the QML engine,
and we should be watching for deletion not actually deleting it.


(cherry picked from commit e13a30f00f)
2024-03-18 13:50:22 +00:00
l10n daemon script 5fd10faeca GIT_SILENT Sync po/docbooks with svn 2024-03-18 03:38:49 +00:00
Xaver Hugl e8f567b055 platformsupport/scenes/opengl: don't access std::nullopt
(cherry picked from commit af3bf939c5)
2024-03-17 17:44:07 +00:00
Xaver Hugl f1c132d329 platformsupport/scenes/opengl: advertise formats unnknown to KWin too
While KWin may not have information about the formats, that doesn't mean KWin
should filter them out - EGL can still import them, so allow clients to use them


(cherry picked from commit 2a13a33040)
2024-03-17 14:20:54 +00:00
l10n daemon script 26537fecfe GIT_SILENT Sync po/docbooks with svn 2024-03-16 03:44:48 +00:00
Harald Sitter 24610e67a3 keyboard_layout: always expose dbus interface
our plasmoid only listens to signals when the interface was found.
also when switching from 2 to 1 layout we'd emit a signal that the
layouts changed but then we'd throw away our interface leaving
the client wondering what happened to us (and consequently
printing warnings because our service wasn't found)

this specifically resulted in the plasmoid not getting layout event
changes when switching from 1 to >1

BUG: 449531


(cherry picked from commit 645db7fc90)
2024-03-16 02:25:50 +00:00
Méven Car 84412b22df wayland: DrmLeaseDevice, use Q_OBJECT macro
BUG: 483008
(cherry picked from commit aa465efa51)
2024-03-15 17:51:22 +01:00
l10n daemon script be43a33002 GIT_SILENT Sync po/docbooks with svn 2024-03-12 02:52:30 +00:00
Xaver Hugl e83e8503d6 plugins/screencast: fix the cursor being offset after changing the scale
When the scale of a screen is changed, the cursor parameters have to be
adjusted or the client will render it offset to the actual cursor


(cherry picked from commit 3a55351211)
2024-03-11 22:56:26 +00:00
Yifan Zhu 05ad71e9fc xkb: fix testing if on keypad
XKB_KEY_KP_9 is 0xffb9 while XKB_KEY_KP_Equal is 0xffbd and XKB_KEY_F1
is 0xffbe. So XKB_KEY_KP_Equal, instead of XKB_KEY_KP_9, has the maximum
keysym for keypad keys.


(cherry picked from commit 681752ada1)
2024-03-11 15:11:29 +00:00
David Edmundson 783a63e594 wayland: Only send artificial mouse up events for xwayland drags
Seat has to handle two types of drags; ones where clients are updated
through data device, and xwayland version where the drag target has
mouse events sents as pointer events. A mechanism to treat them
differently was introduced, but this former xwayland hack was not
included. We also don't need to send frame events when in datadevice
mode.

This reset of pointer state breaks electron.

(cherry picked from commit 044c9f0154)
2024-03-11 13:50:41 +00:00
David Edmundson 61f65ce98d wayland: Only partially revert send pointer leave on drag
This was changed to match the behaviour of other compositors.
However what kwin did before is more sensible.

Sending a leave event breaks cursor updates by design. See
https://gitlab.freedesktop.org/wayland/wayland/-/issues/444

This reverts commit 8a2d77e0a5
(cherry picked from commit 9302e84b95)
2024-03-11 13:49:59 +00:00
Marco Martin 29bfd35585 Properly intersect the shape with clipRect
That for loop in the end didn't have any effect,
actually compute the final region and return that one


(cherry picked from commit 609833e880)

609833e8 Properly intersect the shape with clipRect
2024-03-11 12:06:09 +00:00
l10n daemon script a54cd2ded4 GIT_SILENT Sync po/docbooks with svn 2024-03-11 02:52:38 +00:00
Xaver Hugl d549fee89e core/colorspace: fix ColorDescription comparisons
They used the sdr gamut wideness (which should've been removed) instead of
the sdr colorimetry

BUG: 482809


(cherry picked from commit 0360ce38e2)
2024-03-09 19:24:04 +00:00
l10n daemon script b225e135a2 GIT_SILENT Sync po/docbooks with svn 2024-03-09 03:03:11 +00:00
Yifan Zhu 369c3cbd7d utils/xcbutils: Don't call toXNative with unsigned integer
Instead use signed integers, to prevent the implicit cast from unsigned
integers to qreal.

BUG: 482687


(cherry picked from commit ea4fa87bc6)
2024-03-08 19:34:06 +00:00
Xaver Hugl cc7d710476 backends/drm: handle dumb buffer target correctly
It's independent of the multi gpu import mode

BUG: 482859


(cherry picked from commit f3d9e5c90c)
2024-03-08 17:08:31 +00:00
Vlad Zahorodnii aef8796c72 Port IdleDetector to QBasicTimer
QBasicTimer is lighter and it properly handles timeout values bigger
than INT32_MAX.

CCBUG: 482077
(cherry picked from commit e2cbed7060)
2024-03-08 13:27:07 +02:00
Vlad Zahorodnii 18882eefde Add timeout assert in IdleDetector
CCBUG: 482077
(cherry picked from commit 18e414443e)
2024-03-08 13:26:59 +02:00
Patrik Fábián ab8345a24c xdgshellwindow: Always update window position and size along all axes when fully miximizing window
BUG: 482086


(cherry picked from commit 40b8637ab8)
2024-03-08 09:11:57 +00:00
Nicolas Fella dbf1edcc41 Fix sending window to all desktops
NET::OnAllDesktops is a special desktop number (-1), desktopForX11Id will not return a desktop for it

When all desktops are requested pass an empty desktop list, the following code will handle it appropriately

BUG: 482670


(cherry picked from commit 7078f4e3af)
2024-03-08 08:32:59 +00:00
l10n daemon script 99cfa50a8d GIT_SILENT Sync po/docbooks with svn 2024-03-08 02:52:55 +00:00
Xaver Hugl 673f9a0ac5 backends/drm: also set legacy gamma after VT switches
Just like with modes, the gamma state is unknown after VT switches, and has
to be explicitly set again to ensure it's correct

(cherry picked from commit e4349536a3)
2024-03-07 13:22:29 +01:00
Xaver Hugl 4a78a5417b backends/drm: don't set gamma with legacy unless really necessary
It seems to have a pretty large performance impact

(cherry picked from commit d1bc39a6ea)
2024-03-07 13:22:29 +01:00
Xaver Hugl 29838e1e5b backends/drm: ignore ctm support on legacy
BUG: 482143
(cherry picked from commit 568f9fb666)
2024-03-07 13:22:09 +01:00
Vlad Zahorodnii 6d5a0ed4d2 Rename Workspace::updateClientArea as Workspace::rearrange
We need to re-arrange layer shell surfaces, compute new struts and adjust
the windows in a single step.

Workspace::updateClientArea() is the best candidate for that, so this change
repurposes that function from computing work areas to a generic relayouting
function.

CCBUG: 482361
(cherry picked from commit b674b458df)
2024-03-07 13:52:10 +02:00
Vlad Zahorodnii b3925a9460 wayland: Fix windows shrinking when output layout changes
When the output layout changes, the Workspace is going to update the
struts and then go through every window and see whether it should be
moved or resized.

On the other hand, the layer shell windows react to output changes on
a timer. Furthermore, it's not synchronized with the workspace rearranging
the managed windows. It means that when Workspace::desktopResized() runs,
the panel struts can be slightly outdated, i.e.

- An output layout change occurs
- Workspace::desktopResized() is called but the struts can be wrong
- some time later, LayerShellV1Integration::rearrange is called, it
  fixes layer shell window geometries and struts
- after the layer shell integration has finished rearranging the
  layer shell windows, it calls Workspace::desktopResized(), but the
  damage had already been caused

With the proposed change, the Workspace and the LayerShellV1Integration
will rearrange the windows in sync.

CCBUG: 482361
(cherry picked from commit a489bfa12c)
2024-03-07 13:52:00 +02:00
Łukasz Patron 7db7c97e41 x11window: Skip strict geometry checks in isFullScreenable()
This addresses an issue where mpv window with keep aspect ratio enabled
cannot be fullscreened due to constrainClientSize() returning size that
doesn't fit full screen area.

NOTE: This has been tested with 3440x1440 display and 1920x1080 video in
      mpv.


(cherry picked from commit 083318dddd)
2024-03-06 19:33:32 +00:00
David Edmundson 9e4d7e54c5 xwayland: Use correct key for key release events
When a key is pressed the string that should be used in the key event
changes depending on the xkb updated state for composed keys.

The key itself should be not be affected.
2024-03-06 12:42:41 +00:00
David Edmundson 18e6256dd6 xwayland: Send to xwayland even when no window is focussed
In the codepath to keep xwayland notified of key presses we have to
check the focussed window is not already an xwayland client. To avoid a
null dereference a guard is added that the focussed window is not null,
however the current code incorrectly returns early intead of skipping
just the relevant check.

BUG: 478705
2024-03-06 12:42:41 +00:00
Jonathan Riddell e516aed779 update version for new release 2024-03-05 23:23:29 +00:00
Jonathan Riddell 26cacb79c8 update version for new release 2024-03-05 22:16:52 +00:00
Vlad Zahorodnii fdcc9be2ce effect: Fix EffectWindow::contentsRect()
It should specify the client rect inside the frame.

2556378dfa incorrectly assumed that the
buffer geometry includes the decoration.

BUG: 482294


(cherry picked from commit 8736e44e1a)
2024-03-05 13:57:58 +00:00
Vlad Zahorodnii 7da6ecb3df Fix confined pointer being able to escape the surface
When using fractional scaling, an xwayland window's client geometry can
have some fractional part. When that's the case, .toRect() can shrink the
client geometry or extruding the decoration borders inside the surface.

On the other hand, the pointer is going to be confined as long as the
wl_surface is focused. If the focus jumps to the decoration, the pointer
constraint is going to be broken. Unfortunately, the focus can shift to
the decoration even though the wl_surface is still focused because of the
pesky .toRect().

BUG: 482448
CCBUG: 477124


(cherry picked from commit bfd755aee9)
2024-03-05 13:35:00 +00:00
Dominique Hummel 63a39646ea effects/windowview: use correct enum value for `PointerDevice`
BUG: 482191


(cherry picked from commit d0a6bd3404)

b842efc0 effects/windowview: use correct enum value for `PointerDevice`
2024-03-04 23:15:12 +00:00
Xaver Hugl 42eca26802 backends/drm: don't work around pageflips timing out
It doesn't seem to (always) work, and it has caused at least one crash. If the workaround
is needed, it'll have to be implemented in a different way


(cherry picked from commit 599020d8da)
2024-03-04 14:05:53 +00:00
Xaver Hugl 456ed767e6 opengl: assume a minimum of 2ms of render time
When GPUs are barely loaded, render times can spike randomly, likely because it goes
to a lower power state and has to ramp up again when KWin renders. To ensure this
doesn't make KWin drop frames, assume rendering always takes at least 2 milliseconds


(cherry picked from commit 1b10dde569)
2024-03-04 13:20:03 +00:00
Xaver Hugl be9d2fd7d0 backends/drm: fix vblank calculation
Due to some misinterpretation of mode timings, the old method calculated vsync, not vblank,
so the resulting duration was much shorter, which caused frame drops on some systems.

BUG: 482064


(cherry picked from commit da80dd4c84)
2024-03-04 12:41:11 +00:00
Vlad Zahorodnii 84b4bd3ae8 wayland: Fix a crash in DrmLeaseDeviceV1Interface::setDrmMaster()
The case when a resource is destroyed before kwin becomes the drm master
again is not handled. It can leave dangling pointers in m_pendingFds.


(cherry picked from commit 9e77e5038f)
2024-03-04 11:08:20 +00:00
Xaver Hugl d161f042f6 wayland/surface: don't update preferred color description unless it changed
This avoids clients doing unnecessary work


(cherry picked from commit 3b28788592)
2024-03-03 18:24:34 +00:00
Sam James e89919227f Fix ODR violation with MouseClick plugin
```
/var/tmp/portage/kde-plasma/kwin-6.0.49.9999/work/kwin-6.0.49.9999/src/input_event.h:21:7: error: type ‘struct MouseEvent’ violates the C++ One Definition Rule [-Werror=odr]
   21 | class MouseEvent : public QMouseEvent
      |       ^
/var/tmp/portage/kde-plasma/kwin-6.0.49.9999/work/kwin-6.0.49.9999/src/plugins/mouseclick/mouseclick.h:27:7: note: a type with different bases is defined in another translation unit
   27 | class MouseEvent
      |       ^
lto1: some warnings being treated as errors
```

Bug: https://bugs.gentoo.org/921558
Signed-off-by: Sam James <sam@gentoo.org>


(cherry picked from commit 53a61dfac0)
2024-03-03 10:54:39 +00:00
l10n daemon script b352c5b9ef GIT_SILENT Sync po/docbooks with svn 2024-03-03 03:06:10 +00:00
l10n daemon script c707d87b4d SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-03-03 03:01:51 +00:00
l10n daemon script 098a374fab GIT_SILENT Sync po/docbooks with svn 2024-03-02 02:58:55 +00:00
Akseli Lahtinen 565faf1713 plugins/overview: Adds a border around hovered and selected desktop in desktopGrid
Currently desktop grid view is missing the selection rectangle when hovered over a desktop.
This adds the selection rectangle on hover, since it used to be there before three-state design.

Currently selected desktop has thinner and different colored border.


![image](/uploads/ad1f2f7acbe602fb7b9479ce859026b7/image.png)

![image](/uploads/9fa50bac4b81958da0b8926f8d2518ba/image.png)

![image](/uploads/3e46c1f5a740c40bbf18a5d887f31d39/image.png)

BUG:481812


(cherry picked from commit 181ce590a9)

9b22e56c Adds a border around hovered desktop in desktopGrid
0566df70 Simplify code
f4a4fd4c Use different color and spacing for already selected desktop
99a7fc51 Show borders only in desktop grid view
6c12f24a fix indent
a8981c94 Add parenthesis to ternary expressions, remove stray line
075b6117 Apply 1 suggestion(s) to 1 file(s)
2024-03-01 12:05:50 +00:00
l10n daemon script ee3d84dffc GIT_SILENT Sync po/docbooks with svn 2024-03-01 03:39:37 +00:00
Vlad Zahorodnii aa741db0e7 opengl: Harden GLRenderTimeQuery against opengl providing bad timestamps
The end render timestamp can be slightly in the past before the start
render timestamp. This results in negative render times, which can make
kwin wait way more than just one vblank interval before starting the
next frame.

It appears that there is no way to detect if the gpu has performed a
disjoint operation in OpenGL. It's available only in GLES. As a way
around, this change makes the GLRenderTimeQuery insert two probes: one
queries gl timestamps when starting rendering and ending rendering;
another one just queries std::steady_clock before and after painting.
This hardens the GLRenderTimeQuery against OpenGL providing nonsensical
results sometimes.

BUG: 481721


(cherry picked from commit 0dc3f4906f)
2024-02-29 18:16:34 +00:00
Xaver Hugl 27d215357c backends/drm: delay cursor updates with adaptive sync
While the primary plane delivers a high enough refresh rate for the cursor to be
at least usable, delay cursor updates to be in sync with the primary plane. This
avoids stutter of the content being shown on the primary plane.

Because amdgpu doesn't handle this correctly at the moment, this feature is guarded
behind the KWIN_DRM_DELAY_VRR_CURSOR_UPDATES environment variable.
See https://gitlab.freedesktop.org/drm/amd/-/issues/2186 for more details on that.

(cherry picked from commit 039fd39e34)
2024-02-29 17:58:04 +00:00
Xaver Hugl 803009848c backends/drm: move committing logic into a separate method
This makes the code a bit more readable

(cherry picked from commit 080d28b3f9)
2024-02-29 17:58:04 +00:00
Vlad Zahorodnii 63d7a29e84 backends/x11: Make SwapEventFilter report presentation feedback to OutputFrame
(cherry picked from commit 79b7545840)
2024-02-29 17:44:45 +00:00
Marco Martin 69b64ef78d Remove invalid tabbox configs from defaults
We know that org.kde.breeze.desktop is invalid as LayoutName for the
tabbox, it will break the default button in the tabbox kcm.
Also the DesktopListLayout and DesktopLayout aren't valid anymore

BUG:481640
2024-02-29 12:25:47 +01:00
Fushan Wen 4145d4a26d plugins/outputlocator: show physical size in output locator
Make sure the effect shows the same sizes as the KScreen KCM.


(cherry picked from commit d377b5cdab)
2024-02-29 09:44:39 +00:00
l10n daemon script 5aea602058 GIT_SILENT Sync po/docbooks with svn 2024-02-29 03:01:27 +00:00
l10n daemon script 85e7e617f5 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-02-29 02:56:39 +00:00
l10n daemon script 73d2860647 GIT_SILENT Sync po/docbooks with svn 2024-02-28 02:55:55 +00:00
l10n daemon script ee4b772007 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-02-28 02:51:10 +00:00
Patrik Fábián 018a1d35db plugins/overview: Search bar can be clicked without closing effect
(cherry picked from commit c11eeabc86)
2024-02-27 16:18:17 +00:00
Xaver Hugl 14b96e67e9 autotests/test_colorspaces: add an autotest for non-normalized primaries 2024-02-27 16:57:20 +01:00
Xaver Hugl f20ce6244c core/colorspace: normalize XYZ values before using them in calculations
Otherwise ICC profiles where the primaries are normalized to different values than what
KWin needs may cause too dark or bright results.

BUG: 481034
2024-02-27 16:57:18 +01:00
Xaver Hugl 75b741128e comopsitor: only activate VRR if the active window is on the current screen
BUG: 481750
(cherry picked from commit af4e12c5fc)
2024-02-27 15:33:58 +01:00
l10n daemon script 5bf74d9384 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-02-27 02:59:41 +00:00
Vlad Zahorodnii 5b263b41d5 xwayland: Ignore somebody else claiming WM_S0 selection
If somebody else claims the selection, it's unclear what we should do.
kwin can't give up its selection as it's the one who is responsible for
window management and compositing. If anything, Xwayland should ensure
that no client can claim WM_S0.


(cherry picked from commit 329c5de451)
2024-02-26 14:11:47 +00:00
Vlad Zahorodnii 4ae0f0cd65 Fix a crash in eglDestroyImageKHR()
Apparently, libepoxy requires a current EGL display in order to resolve
EGL functions. This is unexpected as most of the EGL functions require no
current opengl context.

This change makes kwin bypass libepoxy for problematic cases.

BUG: 470980


(cherry picked from commit 13c092d671)
2024-02-26 13:49:05 +00:00
Ser Freeman 7a36938672 Window: Don't reset quick tile mode on interactive move start
Quick tile mode reset should be done in the interactive move handler,
where the new move offset is set. Only reset it on resize to unsnap
the window from the tile.

BUG: 472366


(cherry picked from commit 192ec437df)
2024-02-26 11:27:49 +00:00
David Edmundson ebce0e3c33 x11window: Disable strict geometry placement by default in wayland
Strict geometry disables certain user activites; full screen requests
and only sending configure events at sizes the client claims to support.

This was added as a workaround for xterm in 19 years ago. It's a client
side bug as applications can still choose to ignore the configure event,
so kwin shouldn't have to sanitise them in advance. xterm seems to have
fixed it's bug, and pragmatically we know not all window managers
perform these checks so most clients should not be relying on it.

On Wayland this additional check is proving problematic, the handling of
scaling especially fractional scaling is hitting cases where it's better
to always ask the client to do what kwin wants.

Tests that refer to sizeIncrements are dropped as they are only used in
the strict geometry passes which is being obsoleted. Resizing in general
is still tested.

BUG: 481456


(cherry picked from commit 32be54b19d)
2024-02-26 11:15:08 +00:00
Akseli Lahtinen bd71e48327 tileseditor: Don't allow tiles to move tiles already at minimum size
Previously a tile could intersect with the tile next to it when resizing tiles in tiles-editor.

This checks that the tile next to the resized one is not at minimum size already, and stops
resizing if it is.

Do note that any current setups that may get broken if there is tiles that intersect each other,
so they may have to remake the tile setup.

BUG: 465721
FIXED-IN: 6.0.1


(cherry picked from commit 024db60ccd)
2024-02-26 10:17:43 +00:00
l10n daemon script 9844badc93 GIT_SILENT Sync po/docbooks with svn 2024-02-26 03:01:01 +00:00
Aleix Pol Gonzalez fbe35105ac wayland: Install the clientconnection.h header file
Together with the rest of files that effects might use.

Signed-off-by: Falko Becker <falko.becker@mbition.io>


(cherry picked from commit 7468465393)
2024-02-25 23:07:40 +00:00
l10n daemon script 3397b86108 GIT_SILENT Sync po/docbooks with svn 2024-02-25 03:08:49 +00:00
l10n daemon script 866b0bf142 GIT_SILENT Sync po/docbooks with svn 2024-02-24 02:51:58 +00:00
Xaver Hugl 10eac23ed2 useractions: don't interact with deleted windows
BUG: 481688


(cherry picked from commit d51b69d6ac)
2024-02-23 22:28:19 +00:00
Vlad Zahorodnii 66af39ec9e plugins/showpaint: Add support for color management
It makes the show paint plugin work when color management is enabled.


(cherry picked from commit 56d3c797cc)
2024-02-23 18:45:11 +00:00
Vlad Zahorodnii 515f60cb7f plugins/glide: Fix rotation order when applying render target transformation
The perspective projection matrix has its y axis flipped vertically. It
should be undone when applying the render target transformation, otherwise
the rotation order will be wrong.

BUG: 481664


(cherry picked from commit 7c0a88f34b)
2024-02-23 14:51:15 +00:00
Marco Martin 111cd842f1 Send tablet input to active screen
Try to send the tablet input to the configured screen for the tablet if any,
otherwise always send it to the active screen, not the screen of the active window
which is very confusing

BUG:479713
2024-02-23 12:52:55 +01:00
l10n daemon script 6de7a85083 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-02-23 02:50:44 +00:00
Vlad Zahorodnii df4b125506 backends/x11: Avoid calling doneCurrent() if GLX context has not been created
BUG: 477854


(cherry picked from commit ff4cfe279a)
2024-02-22 15:57:37 +00:00
Vlad Zahorodnii cb3dadf283 backends/wayland: Guard against failing to create EGLSwapchain
BUG: 478864


(cherry picked from commit bb354de90e)
2024-02-22 15:25:23 +00:00
Xaver Hugl 80e30821af backends/drm: only enable HDR if both display and driver are capable of it
The config may have HDR or WCG enabled, and the driver or display settings changed
since then to make it impossible to actually enable either.
The config values stay unchanged when this happens, so reconnecting the display with
HDR support will automatically turn it on again.

BUG: 481518


(cherry picked from commit d33ab542db)
2024-02-22 13:08:56 +00:00
zoro wk 2f4480e60b xcbutils: arm x apps can't be dragged to a negative position on the screen
the behavior of converting negative floating-point numbers to unsigned
integers is undefined. It is necessary to keep the conversion behavior
consistent between ARM and x86 platforms.

through the above conversions, all become the two's complement of
negative numbers.

Signed-off-by: zorowk <near.kingzero@gmail.com>


(cherry picked from commit 904c33556b)
2024-02-22 10:35:06 +00:00
Vlad Zahorodnii 47973b4f1c wayland: Avoid rearranging layer surfaces when wl_surface size changes
wl_surface size is not used when re-arranging surfaces. It also results in
excessive configure events.

Note that it can be useful for updating strut rects, but we could use the
next geometry, which is even preferrable over the frame geometry as the
Workspace would use proper struts earlier.


(cherry picked from commit 648cfcd66c)
2024-02-22 10:04:33 +00:00
l10n daemon script 1ddcb4e288 GIT_SILENT Sync po/docbooks with svn 2024-02-22 02:56:29 +00:00
l10n daemon script adf58ef8d3 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-02-22 02:53:06 +00:00
Xaver Hugl 961a2d7041 opengl/eglcontext: tell Qt when the OpenGL context gets changed
Otherwise, Qt thinks the old context is still current and will do things like
destroying VAOs with KWin's context, which ends up destroying the VAO of the
context and breaks rendering.

BUG: 479094


(cherry picked from commit 8c3332f619)
2024-02-21 20:16:39 +00:00
Jonathan Esk-Riddell 2b8d404a31 Update Frameworks version requirement to 6.0.0
GIT_SILENT
2024-02-21 14:43:11 +00:00
Vlad Zahorodnii fff63dadd4 Remove all legacy virtual desktop ids
Amends 0bd65de375.


(cherry picked from commit 2c445ebf3c)
2024-02-21 13:01:36 +00:00
David Edmundson 8a2d77e0a5 Revert "wayland: Send wl_pointer leave before data_device enter"
This reverts commit a706490763
2024-02-21 11:55:42 +00:00
Jin Liu 38a03f3d3d plugins/contrast: fix: effect not clipped to region
Introduced in:
7732f0e56b


(cherry picked from commit c2c19fe91f)
2024-02-21 11:23:48 +00:00
Vlad Zahorodnii d26d95ed3b plugins/overview: Make screen edge toggle overview rather than cycle between modes
The user may have no usecase for the grid view mode, i.e. they may want
to activate overview, do their thing, and then return back to normal
desktop. However, with the current behavior, there's one extra step
(switch to grid mode) in order to go back to desktop.

In hindsight, we should have added different screen edge actions for
overview and grid modes. This can be done in 6.1.

BUG: 481335


(cherry picked from commit f77f5b2342)
2024-02-21 10:06:25 +00:00
Xaver Hugl 09e4243513 useractions: remove legacy virtual desktop number from the menu
In almost all cases it's duplicated in the name, and if the user manually
changes the name, the custom name should be shown without additional numbers

CCBUG: 481576


(cherry picked from commit 0bd65de375)
2024-02-21 09:19:41 +00:00
l10n daemon script 04ef67ef16 GIT_SILENT Sync po/docbooks with svn 2024-02-21 02:58:05 +00:00
l10n daemon script 26ce318518 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-02-21 02:53:12 +00:00
Vlad Zahorodnii 6e7088d047 Fix disappearing software cursor in overview and zoom effect
The overview and the zoom effect repaint the whole screen every frame.
But the problem is that there are cases when there's nothing to repaint.
I'm not quite sure why it happens. Maybe overview and other effects
should honor the repaint region, but on the other hand, they don't need
to because of the fullscreen effect api promises.

This change forces overview and zoom effect to use "generic paint" code
path in the workspace scene to force infiniteRegion() repaint regions.

BUG: 481523
(cherry picked from commit bb8b51a3ab)
2024-02-21 01:21:44 +02:00
Vlad Zahorodnii 5e3d9fa52a plugins/wobblywindows: Ignore PAINT_SCREEN_TRANSFORMED
Screen transform apis have been dropped, so the wobbly windows should be
safe to ignore screen transformations.

(cherry picked from commit b2450cfe14)
2024-02-21 01:21:35 +02:00
Michael VanOverbeek dde3219c69 Fix zoom push mouse tracking on multi-monitor workspaces
Zoom push tracking now considers the layout of the user's monitors, accounting for situations where the monitor layout doesn't form a perfect rectangle. These changes help prevent the zoom area from being unable to reach certain areas of the workspace depending on which edge of which screen the user pushes against.

One known issue is that, if the mouse moves too quickly, the zoom area can sometimes imperfectly track the movement. It will look the same as the original bug (areas of the screen will appear to be cut off/unreachable), but moving the mouse in the opposite direction a tiny bit snaps the zoom area back to where it should be. 

BUG: 467182

@teams/qa Heads-up that I'm very blind, and this is the first time I've ever contributed to a KDE project. I've tested the changes on my system and they fix the bug, but I want to make sure I didn't break anything in the process.


(cherry picked from commit 86f0d9914e)
2024-02-20 20:57:33 +00:00
Jakob Petsovits d062ce8750 backends/drm: Undo fade-out effect upon unsuccessful DPMS Off
DrmOutput::setDrmDpmsMode() already takes care of reverting any
pending output pipeline changes, but the aboutToTurnOff signal from
setDpmsMode() needs an explicit wakeUp signal to cancel it out.

BUG: 477916
CCBUG: 481520


(cherry picked from commit 9e3e567592)
2024-02-20 19:43:13 +00:00
Nicolas Fella cc057c5865 Set componentDisplayName for shortcut migration
Otherwise the binary name is used as display name, causing all kwin
shortcuts to be registered under kwin-6.0-delete-desktop-switching-shortcuts

(cherry picked from commit afa450b97a)
2024-02-20 12:40:34 +01:00
l10n daemon script 454f48f345 GIT_SILENT Sync po/docbooks with svn 2024-02-20 03:00:39 +00:00
Xaver Hugl d1b5062709 autotests/integration/outputchanges: add geometry restore test
(cherry picked from commit 8fa782f1a8)
2024-02-19 17:30:41 +01:00
Xaver Hugl 9c63c04c13 placementtracker: save geometry restores more explicitly
Geometry restores were only saved when maximize or fullscreen changed, which is
not the only time the geometry restores change. This adds a signal to Window for
that, which fixes a few bugs with windows being moved between screens while
maximized or fullscreened

(cherry picked from commit 2c280b1bb0)
2024-02-19 17:30:41 +01:00
Xaver Hugl e9004f723a placementtracker: don't set geometry to geometry restores
It can cause problems when the geometry restore value is invalid

BUG: 473602
(cherry picked from commit 86db3b4336)
2024-02-19 17:30:41 +01:00
David Edmundson e4443c32b3 overview: Only handled input events in on-screen desktops
kwin does not support true multiscreen drag and drops. Events are sent
to an offscreen location of the screen initiating the drag. Therefore it
is important that off-screen items do not process drop events

BUG: 481331


(cherry picked from commit 3ede995b27)
2024-02-19 15:42:22 +00:00
Vlad Zahorodnii 84e9a6e69b Synchronize input transformation regardless of interactive move resize status
This check is old and I'm not sure why it even exists. It should not be
needed in general.

The problem with this check is that when interactive move/resize
operation finishes, the seat input transformation is not synchronized.


(cherry picked from commit 64e701fdaf)
2024-02-19 15:28:03 +00:00
Niccolò Venerandi dc05316de8 Ensure that translations never change the size of a contrast effect / blur regions
BUG:480434


(cherry picked from commit 7732f0e56b)
2024-02-19 11:23:33 +00:00
l10n daemon script 4f6daadf83 GIT_SILENT Sync po/docbooks with svn 2024-02-19 03:00:44 +00:00
l10n daemon script 19c49093f2 GIT_SILENT Sync po/docbooks with svn 2024-02-18 02:59:31 +00:00
l10n daemon script 42e57a852f GIT_SILENT Sync po/docbooks with svn 2024-02-17 02:59:24 +00:00
Xaver Hugl 09fbf4dac7 backends/drm: don't advertise support for wide color gamut on Intel
i915 doesn't implement the Colorspace property correctly, so exposing this
feature makes it look like our HDR implementation is broken. This hides the
HDR checkbox in system settings until Intel fixes their driver

For testing purposes, users can still opt into this with the environment
variable KWIN_DRM_ALLOW_INTEL_COLORSPACE.


(cherry picked from commit 742268bfa1)
2024-02-16 12:17:33 +00:00
Xaver Hugl c84739d51e backends/drm: force a modeset when connectors are unplugged
And turn off unused CRTCs with legacy

BUG: 478476


(cherry picked from commit 9ea6f311ea)
2024-02-16 11:41:29 +00:00
Xaver Hugl 9e4c3b525a backends/drm: fix HDR with legacy modesetting
The properties were entirely ignored before


(cherry picked from commit d69331d186)
2024-02-16 11:19:46 +00:00
Vlad Zahorodnii 58f33b03a2 plugins/overview: Allow switching between modes using shortcuts while already active
Overview and Grid modes have shortcuts assigned to them. While they
provide a way to toggle the overview effect between on and off state, in
other words overview <-> off or grid <-> off, it's not possible to move
between the modes by pressing those shortcuts, e.g. off -> overview ->
grid -> overview -> grid -> ... -> off.

The culprit seems to be that EffectTogglableState has two "inactive"
states - Inactive and Stopped. It's counter-intuitive and needs a further
cleanup.

To make switching between overview modes work, this change makes
EffectTogglableState::toggle() toggle the state based on the Active state.
There's only one active state.

CCBUG: 481335


(cherry picked from commit fc92544cb3)
2024-02-16 09:06:44 +00:00
Ismael Asensio 8756591037 kcms/rules: Fix section header and info button sizing
The section header being now a QQC2.ItemDelegate needs to explicitly
set the width to be visible.

The ContextualHelpButton size was getting constrained by the parent
layout's being adjusted to just the label height.


(cherry picked from commit 0991cded05)
2024-02-16 08:55:31 +00:00
l10n daemon script 6394a32162 GIT_SILENT Sync po/docbooks with svn 2024-02-16 03:04:08 +00:00
Vlad Zahorodnii c707ce77d7 plugins/screencast: Make grabTexture() more nicer to memfd code path
OutputTransform::Normal is handled by doGrabTexture().

If the texture transform is neither normal nor flip-y, the GPU is going
to be used to transform the texture, however since it doesn't flip the y
axis, doGrabTexture() will flip the y axis on the cpu side. To fix that,
make the contents of backingTexture mirrored vertically.


(cherry picked from commit 7297e62283)
2024-02-15 14:47:55 +00:00
Vlad Zahorodnii deeb0672bf plugins/screencast: Make region screen cast nicer to memfd
Set the FlipY flag so when the memfd code grabs the texture contents, it
doesn't need to flip the texture on the cpu side.

(cherry picked from commit af12a103ae)
2024-02-15 16:18:27 +02:00
Vlad Zahorodnii 1ce25f7137 plugins/screencast: Fix window screencasts being vertically mirrored with memfd
WindowScreenCastSource::render(GLFramebuffer) renders windows with the y
axis flipped, but the offscreen has no FlipY flag set.

BUG: 478223
(cherry picked from commit 3388b7643c)
2024-02-15 16:18:19 +02:00
Xaver Hugl 60849214b3 wayland/textinput_v2: copy the data instead of assuming ownership
The life time of the wl_array is limited to the callback, afterwards it's
a dangling pointer

BUG: 481239


(cherry picked from commit 3a8ae60f87)
2024-02-15 12:23:25 +00:00
David Edmundson a706490763 wayland: Send wl_pointer leave before data_device enter
SeatInterface currently has a separation of kwin's focus scope to
pointer input with early return guards in notifyPointerEnter and
notifyPointerLeave where clients don't get pointer events.

However we don't update the initial state when a drag is started, this
patch notifies sends a pointer leave to the new drag target before the
data_device enter so things are consistent.

This also brings it in line with Weston and Mutter.

notifyPointerLeave has it's early return removed as for wayland windows
as we know nothing will have pointer focus.


(cherry picked from commit 5386360928)
2024-02-15 12:06:23 +00:00
Vlad Zahorodnii 58f38ff522 opengl: Invalidate cached vbo when content transform is different
When output rotation changes, the texture size stays the same, but the
texture coordinates are no longer valid and have to be recomputed.


(cherry picked from commit 5d787a4083)
2024-02-15 11:46:47 +00:00
David Edmundson cc3d5ea89d decorations: Avoid opaque areas of decorations being larger than the rendered contents
Opaque is a QRegion in logical pixels, using .toRect will round to the
nearest integer in either direction. This can mean an area is considered
opaque outside the rendered area, leading to glitchy contents on
shadows.

This is most noticable on on X11 windows when fractional scaling is
used.

Long term I hope to move Item::opaque to QList<QRectF> and
WindowPrePaintData::opaque to device pixels.

(cherry picked from commit c6a3d76b92)
2024-02-15 11:33:29 +00:00
David Edmundson 8becf3cd68 wayland: Dispatch mouse events to internal windows via QWindowSystemInterface
QWindowSystemInterface goes via QGuiApplication which updates some
internal properties. Most notably QGuiApplication::lastCursorPosition
which is used by advanced menu closing behaviour.

BUG: 478061
(cherry picked from commit b36894884e)
2024-02-15 11:33:29 +00:00
Marco Martin 5bf066ff11 Only show otherScreenThumbnail if we are actually dragging
otherScreenThumbnail is used to fake a window thumbnail being dragged
half in the old screen, half in the "new" one (or even more than two)

right now the condition to use it is purely the "real" thumbnail x
or y change, but sometimes especially when the item has just been created
and is being laid out it might trigger an itemDraggedOutOfScreen
when no-one was dragging.

We should never make otherScreenThumbnail visible when we aren't dragging
a thumbnail, so check for Drag being active in the source item.

the item will be kept visible even if drag becomes inactive as before,
as it still needs to be visible for the reset animation

BUG:480564
2024-02-15 11:01:02 +01:00
Fushan Wen 0aaf3e8c84 plugins/colorblindnesscorrection: set translation domain in config ui
By default the translation domain of KQuickManagedConfigModule is the
unique identifier within the namespace of the plugin, but those i18n
strings belong to kwin.po.

(cherry picked from commit f303ba4b59)
2024-02-15 00:40:01 +00:00
Xaver Hugl 160aa7dcba window: use normal keyboard modifiers for triggering custom tiling
Modifiers for global shortcuts are handled differently from normal shortcuts,
because they need to consider modifiers that are consumed by xkb for keyboard
layout transitions and similar. This restriction is not relevant for custom
tiling.

BUG: 465858
(cherry picked from commit 1f1c54ca6c)
2024-02-14 17:39:26 +01:00
Xaver Hugl 6fff1c9d2c input_event: remove modifiersRelevantForTabBox
It was only needed because the normal modifiers path considered caps lock
as shift lock

(cherry picked from commit a917d1885e)
2024-02-14 17:39:26 +01:00
Xaver Hugl 668f6a6978 xkb: caps lock is not shift lock
Caps lock only locks capitalization of letters, making it artificially also
trigger shift in the context of KWin only causes problems

(cherry picked from commit 8c543dbe7c)
2024-02-14 17:39:26 +01:00
Vlad Zahorodnii e1bf40976c Adapt to NETRootInfo::moveResize() changes
(cherry picked from commit 8a9cb06b41)
2024-02-14 11:03:34 +00:00
David Edmundson cab4fdaad2 overview: Explicitly reset parent on teradown
Instantiators create objects when they're added to a model, and
deference when when they're removed from the model.

When we explicitly set a parent in onObjectAdded we're creating a second
reference. This does get cleaned up later, but not in the same frame.

This brings us in line to what QQmlRepeater (which works with items)
does internally for items being added and removed.

BUG: 478777


(cherry picked from commit eab90b6a0a)
2024-02-14 10:08:26 +00:00
l10n daemon script bae9233f7a GIT_SILENT Sync po/docbooks with svn 2024-02-14 03:02:00 +00:00
Yifan Zhu 4727a7d956 screenedge: don't reduce approachGeometry
Reducing approachGeometry is not needed.

During edge creation, createHorizontalEdge and createVerticalEdge
already substract the needed cornerOffset, and edges reserved by client
windows shouldn't be reduced in geometry.
In addition, during display, ScreenEdgeEffect confines the painted area
to approachGeometry. So approachGeometry shouldn't be reduced in
advance.

BUG: 481282


(cherry picked from commit 2f4db693e3)
2024-02-13 15:41:03 +00:00
Ismael Asensio b9bf9d7d41 tabbox/thumbnail-grid: Activate on thumbnail click when selected
While tabbox switching is usually a keyboard operation, we offer also
a mouse-friendly way to launch it via screen edges, and should allow
to switch directly on mouse click.

BUG: 481267
FIXED-IN: 6.0


(cherry picked from commit f6447ad188)
2024-02-13 13:18:49 +00:00
Vlad Zahorodnii 4b7614f04a Fix a warning about null sender in QObject::connect()
The original assumption was that once PointerInputRedirection starts
pushing cursor sources to cursors, they cannot be null. In many cases,
it is true, but the tests are a bit special as many of them lack
wl_pointer.set_cursor requests, so it's possible to set a null source.
As an example, when the pointer moves from the decoration to the
surface. On the other hand, it also does make sense to allow having
no source connected.


(cherry picked from commit 55ef69645c)
2024-02-13 12:59:59 +00:00
l10n daemon script 7f4535490c GIT_SILENT Sync po/docbooks with svn 2024-02-13 03:05:42 +00:00
Vlad Zahorodnii 6ff5b6361c plugins/blur: Fix blurred region sticking outside panel popups
The blur geometry is scaled in the global coordinate space, while it
works fine with integer scale factors, it's not okay with fractional
scale factors as it doesn't match how the ItemRenderer snaps quads to
the pixel grid.

Note that blur behind apps can be still off by one pixel, but it
should be harder to notice it. In order to fix it, it would be great
to apply effects behind Items, which is on my todo list.


(cherry picked from commit 9abf7a9d61)
2024-02-12 14:57:15 +00:00
Xaver Hugl e4f53d983c kscreenintegration: fix rotation not being preserved from Plasma 5
(cherry picked from commit 2ca1d3fd4c)
2024-02-12 14:23:18 +00:00
Vlad Zahorodnii 2e8d386042 scene: Fix DecorationItem reacting to new geometry
Window::layoutDecorationRects() uses KDecoration2::Decoration::rect() to
get the bounding decoration rect.

While Decoration::rect() should normally match Window::rect(), they can
diverge for a brief moment during async geometry updates. The worst
possible case is that the cached item quads may not be invalidated when
the geometry updates saddle.

To fix that, make DecorationItem monitor decorated client size changes
instead of window frame geometry changes. The reason for that is that
Decoration::size() is effectively decorated client size with added border
margins.


(cherry picked from commit 857766eb74)
2024-02-12 13:38:33 +00:00
Vlad Zahorodnii 26b605d4d9 plugins/overview: Fix a warning about incorrect anchor
The mouse area is no longer a sibling of the window heap, which produces
a warning.

BUG: 481106


(cherry picked from commit b02190bf23)
2024-02-12 11:20:57 +00:00
Vlad Zahorodnii d265fe2ad5 wayland: Avoid setting tiled_ xdg-toplevel state
Resizing of tiled GTK apps is broken because GDK relies on some information
provided by the gtk-shell protocol. There is an upstream fix, but it has
not been merged yet.

5.27 is affected too, but unlike 6.0, wayland is not enabled there by default.
So let's disable tiled_ state to fix resizing GTK apps. As far as I know,
GTK is the biggest consumer of tiled_ xdg-toplevel state at the moment, so
hopefully no other apps are going to be affected by this change.
2024-02-12 11:41:32 +02:00
l10n daemon script 5f0c20503e GIT_SILENT Sync po/docbooks with svn 2024-02-12 02:59:39 +00:00
l10n daemon script 7f099cac6c GIT_SILENT Sync po/docbooks with svn 2024-02-11 03:05:57 +00:00
l10n daemon script ad71ff1dfa SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-02-11 02:59:21 +00:00
l10n daemon script ad3268b0b8 GIT_SILENT Sync po/docbooks with svn 2024-02-10 03:23:35 +00:00
l10n daemon script 168e25ee38 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-02-10 03:17:30 +00:00
Vlad Zahorodnii 7644499e20 Handle wl_surface destruction in SurfaceCursorSource
Wine/Wayland hides the cursor as follows:

[ 853107.473]  -> wl_pointer@15.set_cursor(172832, wl_surface@38, 0, 0)
...
[ 858989.757]  -> wl_surface@38.destroy()
[ 858989.759]  -> wl_pointer@15.set_cursor(172832, nil, 0, 0)

i.e. it destroys the cursor surface, then calls wl_pointer.set_cursor().

SurfaceCursorSource stores the wl_surface in a QPointer, furthermore it
is going to emit the changed signal, which is needed to force the
CursorItem to update its content, only if either a new hotspot or a
surface has been passed to SurfaceCursorSource::update(). So what happens
is the following:

- The SurfaceInterface object is destroyed and the QPointer resets its
  value to nullptr
- SurfaceCursorSource::update(nullptr, QPointF(0, 0)) gets called in
  response to wl_pointer@15.set_cursor(nil, 0, 0)
- but since m_surface has been implicitly reset to nullptr, no changed
  signal is going to be emitted

This change addresses the issue by making the SurfaceCursorSource track
the SurfaceInterface's destroyed signal.

BUG: 480582


(cherry picked from commit 13e7cac019)
2024-02-09 12:08:17 +00:00
David Edmundson 8b73147fa9 quickeffect: Avoid double delete of QQuickViews
Views are owned by the C++ backend, but also retrievable by invokables
to get neighbouring screens from JS space. By default Qt then transfers
ownership of the view to the QML collector. This results in double
ownership.

BUG: 480788


(cherry picked from commit 95d4671a10)
2024-02-09 09:19:26 +00:00
Vlad Zahorodnii 16031795c0 Fix X11Window wrapper window geometry
This amends 66a491bda6.

There are a few more cases that had been overlooked in the original patch.

(cherry picked from commit 2556378dfa)
2024-02-09 08:36:37 +00:00
Vlad Zahorodnii f231f22546 wayland: Remove extra space around the wrapper window in X11 windows
This way no extra buffer space is going to be wasted for a decoration
that isn't there, and it might be nicer for fractional scaling as kwin
won't need to deal with border size voodoo cases.

(cherry picked from commit 66a491bda6)
2024-02-09 08:36:37 +00:00
l10n daemon script b1f9379266 GIT_SILENT Sync po/docbooks with svn 2024-02-09 03:09:29 +00:00
Ismael Asensio 2d099d6603 kcms/rules: Fix import/export FileDialog type
They were just missing the alias qualifier.

Add also explicit parameters to the signal handlers.

BUG: 481082
FIXED-IN: 6.0


(cherry picked from commit 7c8b5be55a)
2024-02-08 21:57:50 +00:00
Xaver Hugl dd54b03aaf scene/surfaceitem: change refresh rate estimation to frame time estimation
This is both more direct and avoids divisions by durations that can potentially
be zero

BUG: 480971


(cherry picked from commit 31ebdb73a0)
2024-02-08 19:01:22 +00:00
Vlad Zahorodnii 89688db8e9 wayland: Schedule a configure event when borders change
Window::checkWorkspacePosition() before the window is mapped is still
problematic and should be avoided as it can produce undesired constrained
client size (1x1).

Given that XdgToplevelWindow tries to maintain the same frame geometry
size, it should be enough to schedule another configure event instead.
It is going to be in line with the other decoration logic in the
XdgToplevelWindow and it's a better way to handle async geometry updates.

BUG: 480910
(cherry picked from commit e974c07001)
2024-02-08 17:14:21 +02:00
Vlad Zahorodnii 25f5cbcc15 plugins/nightcolor: Rename d-bus interface
(cherry picked from commit 5ad63f21e0)
2024-02-08 17:14:06 +02:00
Xaver Hugl 7f8cf985af wayland: update xx-color-management to v2
(cherry picked from commit a5726e19fd)
2024-02-08 17:13:47 +02:00
Vlad Zahorodnii 4f430bcc7e plugins/overview: Revoke Meta+Tab and Meta+Shift+Tab shortcuts
These shortcuts make more sense to be used with Meta+Tab and Meta+Shift+Tab.
Let's keep them reserved for the task switcher. Cycling between overview
modes is not something that requires Meta+Tab to be assigned to it by default.

(cherry picked from commit cd43199e70)
2024-02-08 17:13:35 +02:00
Vlad Zahorodnii 7d456cd63a kconf_update: Drop kwin-6.0-overview-activities-shortcuts script
There are a few issues:

- it's incompatible with Version 6 format
- activity shortcuts cannot be changed in kwin
- overview shortcuts don't need to be touched

BUG: 480758
(cherry picked from commit 540dff30e7)
2024-02-08 17:13:21 +02:00
David Redondo c34aec1c7b Guard against render time query failing
glGetQuery can fail (for example because of a context loss) in this
case the buffer stays unmodified. In this case this is zero resulting
in GLRenderTimeQuery::result() returning a negative value. Down the
line this leads to a negative duration in the RenderJournal and
RenderLoopPrivate::scheduleRepaint starting a timer with an amount
of milliseconds bigger than what an int can hold. This will not
actually start a timer but QTimer::isActive returns true resulting
in no futher repaints being scheduled.
BUG: 475605
FIXED-IN: 6.0


(cherry picked from commit 6b4018014c)
2024-02-08 13:53:27 +00:00
Vlad Zahorodnii 6168b5dd54 kcms/tabbox: Update layout name in response to user input
The current index can change when populating the combobox.


(cherry picked from commit 4b7874391d)
2024-02-08 13:23:18 +00:00
Marco Martin e824ab00a3 Don't scale WindowHeap in overview mode
when in overview mode, don't scale down WindowHeap, as this
will cause ugly glitches, but resize it down instead.
Still use transforms when it goes in desktop grid mode, at least for now

probably future further refactor can still help things

Before:

![image](/uploads/7ca83e7e9292bd8489faaf76d4c12693/image.png)

After:

![image](/uploads/27b970d056c89486661d6695d09813ff/image.png)

CCBUG:475682
2024-02-08 14:14:53 +01:00
l10n daemon script 38201e5edd GIT_SILENT Sync po/docbooks with svn 2024-02-08 03:01:57 +00:00
Xaver Hugl 43f223afe2 backends/drm: fix night light updates after dpms
BUG: 480911
FIXED-IN: 6.0


(cherry picked from commit 83fe158a16)
2024-02-07 22:50:36 +00:00
Xaver Hugl 590055e8ff backends/drm: fix multi gpu transfers with mixed modifiers and implicit modifiers usage
BUG: 478921
(cherry picked from commit d3a2e07002)
2024-02-07 15:21:48 +00:00
Xaver Hugl e4244c8056 backends/drm: fix EglGbmLayerSurface::doesSurfaceFit with multi gpu
CCBUG: 478921
(cherry picked from commit 1583b2c717)
2024-02-07 15:21:48 +00:00
Vlad Zahorodnii c3eb4ecd04 plugins/backgroundcontrast: Correct texture transform
The y axis should be mirrored as the last step, otherwise it's going to
change the winding order in rotation transforms in the render target
transform.

BUG: 479934


(cherry picked from commit 6d0f1fedc0)
2024-02-07 15:14:27 +00:00
Vlad Zahorodnii d174a6ba33 Prefer up-arrow cursor shape
That's what breeze only has.

(cherry picked from commit a472c90327)
2024-02-07 10:53:15 +02:00
Vlad Zahorodnii 001865d37f Prefer cursor shape names from the CSS W3C specification
There is a mix of cursor shape names from the CSS W3C specification and
non-spec ones, which is confusing when deciding what cursor shapes need
aliases in Cursor::cursorAlternativeNames().

(cherry picked from commit c9c84b6859)
2024-02-07 10:53:07 +02:00
Columbariu S b49eb3d396 plugins/screencast: Announce size and stride only for SHM buffers
DmaBufs don't have a welldefined size or stride. Clients and PipeWire
shouldn't rely on those values. Therefore we better omit them.

(cherry picked from commit 7c2cf9c953)
2024-02-07 10:52:58 +02:00
Columbariu S c9f36137ec plugins/screencast: Reset dmabufParams if SHM buffers are negotiated
m_dmabufParams is initialised after a format with modifiers is
suggested. Should the negotiation process fallback to SHM buffers, we
should reset it, so it can be used to determine if the negotiated format
is of type DmaBuf or not.

(cherry picked from commit cb398ce6fe)
2024-02-07 10:52:50 +02:00
l10n daemon script ca9e55be49 GIT_SILENT Sync po/docbooks with svn 2024-02-07 02:55:04 +00:00
Vlad Zahorodnii 8a932da1f4 Revert "tabbox: Show window switcher only when there are two or more windows"
This reverts commit b31baaf0cd.

It's still a good idea to show the task switcher even if there's only
one window in order to provide the user feedback about their action.
Since the task switcher is not shown when there's only one window, it
can be confusing and lead to thinking that the task switcher is broken.
It also fixes a regression which prevents alt-tabbing to the only
remaining minimized window.

BUG: 480940
CCBUG: 419408


(cherry picked from commit 8086707d1c)
2024-02-06 16:14:06 +00:00
l10n daemon script f6a2821698 GIT_SILENT Sync po/docbooks with svn 2024-02-06 03:41:06 +00:00
Xaver Hugl 7927351bcb backends/drm: add workaround for an amdgpu adaptive sync cursor bug
See https://gitlab.freedesktop.org/drm/amd/-/issues/3034 for details


(cherry picked from commit 3d21e41bc9)
2024-02-06 00:02:41 +00:00
Xaver Hugl bb7d2152ba backends/drm: try to handle page flips timing out
While this should really never happen in the first place, if the kernel still accepts
atomic commits, this is better than the screen(s) freezing and never recovering.

BUG: 480895


(cherry picked from commit 14749e91e9)
2024-02-05 23:00:28 +00:00
Xaver Hugl f94e4d16dd backends/drm: use explicit sync where possible
Instead of calling glFinish, which blocks until it's done and has high CPU
usage on NVidia, use EGL_ANDROID_native_fence_fd to get an explicit sync
fd, which the commit thread automatically waits on before committing the
buffer to KMS.

CCBUG: 452219


(cherry picked from commit 1c8bd1be62)
2024-02-05 22:19:13 +00:00
Vlad Zahorodnii f1e7776cb6 kcms/options: Hide "active mouse screen" option
In the late Plasma 5 times we agreed that it would be better to drop
"active mouse screen" option and stick with last interacted screen
approach instead. However, it was forgotten and nobody has pursued this
goal, so let's hide the option in the system settings ui at least.

The option is not completely removed because some parts of kwin would
need adjustments.


(cherry picked from commit e7d6e8b217)
2024-02-05 18:45:09 +00:00
Vlad Zahorodnii e29f765635 plugins/desktopchangeosd: Fix previous desktop indicator
BUG: 480630


(cherry picked from commit 8f7f97148c)
2024-02-05 18:09:14 +00:00
Vlad Zahorodnii e658d5ee02 Fix syncing Xwayland::Scale config option
The new scale value is written but it's not flushed to the disk so
when kcm_fonts_init and kcm_style_init run, they use the old scale
value.

CCBUG: 480792


(cherry picked from commit 63c1363ca1)
2024-02-05 17:16:51 +00:00
Xaver Hugl 1a6d8d265c scene/surfaceitem: prevent division by zero
If the steady_clock's resolution is very limited, now - m_lastDamage might be zero,
so enforce a minimum time of 10us (or 100'000 fps)


(cherry picked from commit 41aeecbb2a)
2024-02-05 16:51:02 +00:00
Vlad Zahorodnii 9e4d6f4389 effect: Allow ref'ing normal windows
It can be used to simplify state tracking in some effects. The
restriction exists because there used to be separation between normal
and closed windows (Toplevel and Deleted), and one could reference
only Deleted windows. So it was easier just to forbid referencing still
alive windows.

(cherry picked from commit e97b6032c2)
2024-02-05 18:33:48 +02:00
Vlad Zahorodnii 66df775a53 plugins/slidingpopups: Always ref window when sliding it offscreen
Imagine the following case:

- the window is hidden, slideOut() is called but no deleted reference is
  created because the window is not deleted
- the window is closed, slideOut() won't be called because the window is
  hidden and SlidingPopupsEffect::slotWindowClosed() ignores closed windows
  that are already hidden
- the window is deleted in meanwhile
- the sliding popups effects attempt to delete m_animations[w] entry,
  but since "w" is a dangling pointer at this point, visibleRef is going
  to access released memory

To fix that, make slideOut() always ref the window.

(cherry picked from commit 0c03e7ccfc)
2024-02-05 18:33:38 +02:00
Vlad Zahorodnii 0e96bc341f plugins/screenshot: Cache screenshot attributes
When a QPromise reports results, it's not necessary that the
QFutureWatcher is going to report it immediately. That can happen at
some point in the future, which is okay according to the QFuture api
contract.

Due to that, we cannot assume that the stored Output and EffectWindow
objects pointers are valid when the QFutureWatcher::finished is emitted.


(cherry picked from commit 69f344a439)
2024-02-05 16:13:39 +00:00
Aleix Pol 23af6cb5f1 autotests: Bundle pipewiresourcestream with testScreencasting
testScreencasting refuses to pass with kpipewire. We've attempted
various things to debug it, neither makes any sense. Furthermore, the
test passes locally.

As a short term solution, make the test use pipewire directly.
2024-02-05 18:01:17 +02:00
l10n daemon script 065ce0475f GIT_SILENT Sync po/docbooks with svn 2024-02-05 03:38:15 +00:00
l10n daemon script d3fbe753ea GIT_SILENT Sync po/docbooks with svn 2024-02-04 03:02:00 +00:00
l10n daemon script 158d557683 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-02-04 02:55:59 +00:00
l10n daemon script 0bdc3e73aa GIT_SILENT Sync po/docbooks with svn 2024-02-03 03:02:44 +00:00
Vlad Zahorodnii d296e10803 Add fallback cursor shape for "default" shape
Some cursor themes don't have "default" shape, so fallback to "left_ptr"
with such themes.

BUG: 477476


(cherry picked from commit cc2e6fc96c)
2024-02-02 15:18:45 +00:00
David Redondo 8e46556fff Update to merged version of xdg-toplevel-drag
(cherry picked from commit ee782c8a91)
2024-02-02 14:47:50 +01:00
David Redondo 8c03472f8c Revert "Use qt prefix variant of toplevel drag protocol"
The protocol has been merged upstream and Qt is now using it.

This reverts commit 216a268a43.

(cherry picked from commit 4d0cd42aef)
2024-02-02 14:47:50 +01:00
Niccolò Venerandi 1688463cfb Register touch action to activate Overview instead of toggling it
This is because the Overview will activate itself at the end of a 1:1 gesture,
and a toggle action might actually deactivate it rather than activate it.

(cherry picked from commit 79a61deb25)
2024-02-02 14:47:50 +01:00
Vlad Zahorodnii aafce3b54e wayland: Truncate virtual desktop names
Virtual desktop names are user defined strings so they can exceed
the maximum size of a wayland message size.

BUG: 480614


(cherry picked from commit e58451fc01)
2024-02-02 11:51:01 +00:00
l10n daemon script 23fa0041aa GIT_SILENT Sync po/docbooks with svn 2024-02-02 02:55:20 +00:00
Vlad Zahorodnii e566bdfcd7 Activate next window when an X11 window is minimized
It matches the behavior of XdgToplevelWindow.

BUG: 479388
(cherry picked from commit 5896bab86f)
2024-02-02 02:06:22 +01:00
Xaver Hugl e40366b750 xwayland/xwaylandlauncher: don't enable WAYLAND_DEBUG with KWIN_XWAYLAND_DEBUG=0
(cherry picked from commit 880ce92fb2)
2024-02-02 02:06:22 +01:00
Vlad Zahorodnii 74ab969c72 backends/virtual: Fix OutputFrame
(cherry picked from commit c733e7a7b6)
2024-02-02 02:06:22 +01:00
Xaver Hugl 63868e4dd4 x11window: explicitly resize when the Xwayland scale changes
With how Xwayland scaling works, KWin assumes the window already uses the
new coordinate system - but that doesn't happen until Xwayland and the client
know about the new size as well.

BUG: 480642
(cherry picked from commit c2749e3acf)
2024-02-02 02:06:22 +01:00
Vlad Zahorodnii 8ed28586d9 effect: Fix initialization of QEvent::isAccepted() in cloned events in OffscreenQuickView
QEvent::isAccepted() is initialized to true by default.

BUG: 480538
(cherry picked from commit 93b9fdd391)
2024-02-02 02:06:22 +01:00
Xaver Hugl 19d665b216 autotests/integration: re-enable lid closed output changes test
KWin is handling the lid switch now

(cherry picked from commit 67b1a88466)
2024-02-02 02:06:22 +01:00
Xaver Hugl 9180693c50 plugins/nightcolor: clamp preview color temperature to be somewhat sane
BUG: 480700
(cherry picked from commit cc72778d5e)
2024-02-02 02:06:22 +01:00
Yifan Zhu babc295b3c inputmethod&plugins/buttonrebinds: use new KKeyServer API
To correctly handle Qt::Key_Calculator corresponding to both
XF86Calculator and XF86Calculater.

(cherry picked from commit ae7fb3885b)
2024-02-02 02:06:22 +01:00
Jonathan Esk-Riddell 7bcdd78a89 update version for new release 2024-02-01 09:45:38 +00:00
Vlad Zahorodnii 1cab899c5a Take surface idle inhibitors into account only after window is added to the workspace
This helps to reduce having N sources for the same information.


(cherry picked from commit 795b619704)
2024-02-01 08:50:56 +00:00
Marco Martin 44325842b8 plugins/wobblywindows: Use snapping when the window isn't moving
disable pixel snapping when the window is moving with some velocity/acceleration,
reenable it when is (alsmost) stopped


(cherry picked from commit 19970bd639)
2024-02-01 08:49:03 +00:00
Jay Paul 416ed6479d plugins/screencast: set frame timer to one shot
BUG: 469777


(cherry picked from commit 5ae170f1d6)
2024-02-01 08:28:49 +00:00
l10n daemon script 09529f60f6 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-02-01 02:57:33 +00:00
Xaver Hugl d2d054b921 opengl/glshader: make uniform enums type safe
(cherry picked from commit 896a57d3be)
2024-01-31 13:28:07 +01:00
Xaver Hugl fe2ead7644 backends/drm: use the correct uniform type
(cherry picked from commit a649be64db)
2024-01-31 13:28:07 +01:00
Xaver Hugl 10612ba279 backends/drm: merge all commits and try again if atomic commits fail
The failure might be from the commit reordering going wrong in some way.
The total accumulated state might still work even if an individual commit
does not though, so before considering the whole frame lost, merge all the
commits and try again

(cherry picked from commit 6db05aaef1)
2024-01-31 13:28:07 +01:00
Xaver Hugl 19a0a0118e colorspace: make sdr colorimetry not be about rec.2020 anymore
This was just done because of the wrong assumption that displays needed that
to show the full native gamut. That turned out to be an amdgpu bug though; with
that fixed, most of the 0-100% range is wildly oversaturated.
To make the slider more intuitive, this changes the sdr gamut wideness to instead
interpolate to the native display primaries as indicated by the EDID.

(cherry picked from commit 9c0085f5a9)
2024-01-31 13:28:07 +01:00
Jonathan Esk-Riddell ba0ce9ec1a Update version number for 5.93.0
GIT_SILENT
2024-01-31 10:52:47 +00:00
Xaver Hugl a47d6ffe9b input: increase raise timeout for drag and drop to 1s
This should be long enough to not happen accidentally, but short enough to not be
annoying and discoverable.

BUG: 480511


(cherry picked from commit 8d44ece874)
2024-01-31 08:42:00 +00:00
Vlad Zahorodnii 604924b8a9 plugins/screencast: Fix hidden cursors
The screencast plugin doesn't take into account the hidden status of
the cursor, which results in the cursor being visible when screencasting
even though it's hidden.


(cherry picked from commit 3a1b9414ed)
2024-01-31 08:40:22 +00:00
l10n daemon script ee606a1fc6 GIT_SILENT Sync po/docbooks with svn 2024-01-31 03:14:17 +00:00
l10n daemon script 184522822f SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-01-31 03:06:46 +00:00
Vlad Zahorodnii 0691a973f8 Ignore external updates of _NET_DESKTOP_LAYOUT and _NET_DESKTOP_NAMES
At the moment, the desktop layout in _NET_DESKTOP_LAYOUT overwrites new
desktop layout with outdated information. This happens because kwin tries
to honor the desktop layout set by the pager. However, kwin itself
already acts as the pager. The pager applet in plasma doesn't attempt to
maintain _NET_DESKTOP_LAYOUT with proper values.

On the other hand, kwin trying to both update and also sync its state to
_NET_DESKTOP_LAYOUT and _NET_DESKTOP_NAMES has created a series of
issues, like lockups or rendering glitches.

Given that the window manager can ignore these properties, and the fact
that kwin already does act like a pager, this patch makes kwin ignore
external updates to _NET_DESKTOP_LAYOUT and _NET_DESKTOP_NAMES.

In order to modify the desktop layout on X11, use the dbus api. On
Wayland, either the dbus api or the virtual desktop wayland protocol.

BUG: 422319
BUG: 480371


(cherry picked from commit def3a50558)
2024-01-30 14:54:25 +00:00
l10n daemon script 33f1cd8e81 GIT_SILENT Sync po/docbooks with svn 2024-01-30 03:00:18 +00:00
Xaver Hugl d28e89cc6f colorimetry: use 4x4 matrices for colorimetry transforms
This is so that offsets can be represented in the matrices and not just
scaled and rotated coordinate systems

(cherry picked from commit b1414033ef)
2024-01-29 23:16:58 +01:00
Xaver Hugl 9dd0943b0a wayland: implement experimental tag of the upstream color management protocol
Support is hidden by an environment variable to prevent accidental standardization
on this experimental version. It allows app devs to already implement and
test it though, and easily switch to the proper protocol later

(cherry picked from commit 4dd1e91bda)
2024-01-29 23:16:51 +01:00
Vlad Zahorodnii 655733d834 wayland: Install headers generated by qtwaylandscanner
(cherry picked from commit a64c86b73f)
2024-01-29 20:40:34 +00:00
Vlad Zahorodnii aa5fbb7bbb wayland: Add windows when readyForPainting changes
A window is added to the workspace when it's mapped. It's assumed that
the first Window::windowShown signal indicates that. But it's not
entirely true. For example, if setHidden(false); setHidden(true); are
called in succession, the window will be marked as ready for painting
even though it isn't.

The Window::readyForPaintingChanged() signal fixes that. It's emitted
when the window is actually mapped.


(cherry picked from commit e23cb52a16)
2024-01-29 10:49:08 +00:00
Xaver Hugl 2b03587364 backends/drm: don't set the content type drm property
We haven't seen any benefit from passing it through from apps and apparently
it can make atomic commits fail in some cases.

BUG: 480454


(cherry picked from commit b05fa94d32)
2024-01-29 09:56:52 +00:00
Akseli Lahtinen c51a53f93b Set correct opaqueRegion for Xwayland apps
(cherry picked from commit 91974b7794)
2024-01-29 09:45:21 +00:00
Vlad Zahorodnii 7d99853edc plugins/slidingpopups: Avoid sliding already hidden popups
(cherry picked from commit d9e96223f4)
2024-01-29 09:39:06 +00:00
Vlad Zahorodnii 91b01d66b4 Port ScreenEdges::recreateEdges() to std::span
(cherry picked from commit 341067d4d7)
2024-01-29 09:38:38 +00:00
Vlad Zahorodnii 70ddead9d2 Reserve a screen edge on the same output as the window
This makes the association between the window's output and the screen
edge's output more robust.


(cherry picked from commit 16aaf92782)
2024-01-29 09:38:06 +00:00
Vlad Zahorodnii 80cd4bdbb5 Relax constraints for screen edges reserved by windows
At the moment, if the workspace extends or shrinks by an output, the
hidden panel will be shown. It doesn't make sense in all cases.

Furthermore, no screen edge will be reserved if the layer surface has
some margins.

To address that, allow "floating" panels reserve screen edges and also
make kwin try harder to preserve window edges if the output layout
changes.

CCBUG: 448420


(cherry picked from commit 2d399e93f0)
2024-01-29 09:37:25 +00:00
Vlad Zahorodnii 1264672374 plugins/shakecursor: Ignore pointer motion events when the pointer is constrained
The main motivation is to prevent the shake cursor from triggering in
video games. It's not a bullet-proof solution though.


(cherry picked from commit aaa35d455c)
2024-01-29 09:06:12 +00:00
Vlad Zahorodnii 08b3643153 autotests: Disable outline in breeze
Some our tests assume that with "none" border size, the decoration has
no borders. When breeze paints an outline, that's not the case.


(cherry picked from commit 1eb95982dc)
2024-01-29 09:04:48 +00:00
Vlad Zahorodnii e241e3eb9f wayland: Implement closeable window rule
BUG: 443129


(cherry picked from commit b21229e59a)
2024-01-29 09:03:18 +00:00
Vlad Zahorodnii d1083a4db0 effect: Overwrite the output in OffscreenQuickView::setGeometry()
It seems like QWindow::setGeometry() won't update the associated output
if no platform window has been created, which is the case when running
overview or any other qml effect.

It's not clear whether this is a Qt bug or intended behavior.
qwindow.cpp contains comments assuming that the window is on the primary
output if no platform window exists.


(cherry picked from commit 35572904fc)
2024-01-29 08:56:23 +00:00
Vlad Zahorodnii 8ee4484548 effect: Allocate an offscreen fbo with correct scale in OffscreenQuickView
QQuickWindow::effectiveDevicePixelRatio() uses the device pixel ratio
of the attached QQuickRenderTarget. Instead, the scale factor of the
output should be used, which is what QWindow::devicePixelRatio() returns.


(cherry picked from commit 601d33f294)
2024-01-29 08:56:01 +00:00
Fushan Wen be9bd4caea tabbox/switchers/thumbnail_grid: use FocusScope as main item
By default the focus item in a tabbox is always the main item, so if the
main item is not a focus scope, the inner item will not get focused.

BUG: 477286
FIXED-IN: 6.0


(cherry picked from commit 75b08a8fd9)
2024-01-29 08:50:28 +00:00
l10n daemon script a787b39845 GIT_SILENT Sync po/docbooks with svn 2024-01-28 03:00:30 +00:00
l10n daemon script c67601c20b GIT_SILENT Sync po/docbooks with svn 2024-01-27 02:58:15 +00:00
l10n daemon script 8e6648ecf0 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-01-27 02:54:37 +00:00
Vlad Zahorodnii 615fbac7ee autotests: Add layer_surface_v1.set_exclusive_edge test cases
(cherry picked from commit 1b6aa65ee5)
2024-01-26 12:56:00 +00:00
Marco Martin 67e154b101 wayland: Add support for layer_surface_v1.set_exclusive_edge
This can be used to disambiguate the exclusive edge when the anchors are
on a corner (so there would be 2 candidates)

it's quite quick and dirty mostly to understand if we do want to push for
something along the lines (it should at least do a protocol error when
the requested edge is not within the anchors)


(cherry picked from commit fe8fe42ea9)
2024-01-26 11:38:19 +00:00
l10n daemon script ef3d1f2629 GIT_SILENT Sync po/docbooks with svn 2024-01-26 03:01:16 +00:00
Vlad Zahorodnii 9b964088dc plugins/zoom: Unset PAINT_SCREEN_TRANSFORMED
It's not needed because of the offscreen texture.

BUG: 480216


(cherry picked from commit 91ae3d907e)
2024-01-25 23:18:00 +00:00
Vlad Zahorodnii 7f8c41bedc kconf_update: Add script to drop old desktop switching shortcuts
BUG: 479984
(cherry picked from commit 2505fd03f1)
2024-01-25 10:57:08 +02:00
Xaver Hugl 063d0ab81a core/renderloop: take the output of the active window into account for vrr scheduling
If the active window is on a different output than the one the renderloop is for,
the scheduling logic would otherwise never schedule a repaint while adaptive sync
is active.

BUG: 480252
(cherry picked from commit bbc833baa6)
2024-01-25 10:56:58 +02:00
Xaver Hugl 6fba02a75f plugins/screencast: hardcode DRM_FORMAT_ARGB8888 for screencasts
It's universally supported, and our format negotiation code needs improvements to
properly handle the receiving application not suporting the actual output format

(cherry picked from commit bb2391133d)
2024-01-25 10:56:44 +02:00
Nicolas Fella 6ef84f8451 Remove wrong handling of Qt::KeypadModifier
Qt::KeypadModifier doesn't actually map to any XKB modifier

The XKB_MOD_NAME_NUM modifier is (semi-)unrelated

(cherry picked from commit 06230e43d6)
2024-01-25 10:56:27 +02:00
l10n daemon script 824b38a8fd GIT_SILENT Sync po/docbooks with svn 2024-01-25 02:54:29 +00:00
l10n daemon script 56a65284e2 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-01-25 02:51:08 +00:00
Vlad Zahorodnii d6202dcc60 autotests: Add keypad global shortcuts test
(cherry picked from commit 9cdf19c657)
2024-01-24 15:26:56 +02:00
Nicolas Fella ae23df1f53 Consider Qt::KeypadModifier relevant for global shortcuts
Otherwise kglobalaccel can't distinguish between numbers on the num block and other numbers

BUG: 453423

BUG: 446389
(cherry picked from commit 296d3f31be)
2024-01-24 15:26:46 +02:00
Vlad Zahorodnii dd03140289 kconf_update: Update Version
kconf_expects version 6.


(cherry picked from commit 25150633ed)
2024-01-24 10:17:41 +00:00
Vlad Zahorodnii 13c3a499cd Prefer QRegion::operator+=
`QRegion::operator|=` has some optimizations but it basically boils
down to

  QRegion result(*this);
  result.detach(); // it will make a copy because this is shared
  result.d->append(rect);
  return result;

On the other hand, `QRegion::operator+=` tries to add the new rect
in-place.

(cherry picked from commit 3bce89553c)
2024-01-24 10:36:14 +02:00
Vlad Zahorodnii e6685c0d21 plugins/blur: Fix blur behind decoration
Currently, if only blur behind decoration is enabled, no m_windows entry
is going to be created and so the blur effect won't blur the background.

BUG: 479893
(cherry picked from commit f44484137e)
2024-01-24 10:36:04 +02:00
Vlad Zahorodnii bf33a3de15 tests: Fix constraint region in pointerconstraints example
(cherry picked from commit e01116149b)
2024-01-24 10:35:55 +02:00
l10n daemon script 2849f66f97 GIT_SILENT Sync po/docbooks with svn 2024-01-24 02:56:57 +00:00
l10n daemon script 5e691497c1 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-01-24 02:53:12 +00:00
Vlad Zahorodnii de36aad203 Apply "Accepts focus" window rule to WM_TAKE_FOCUS messages as well
Currently the window rule is not 100% effective because the "take focus"
message is not guarded. So depending on the input model of the X11
window, kwin can still activate a window even if it has a window rule
to force no focus.

workspace()->setShouldGetFocus() should be guarded too to help the focus
stealing prevention logic.

Technically though, forcing XSetInputFocus() or WM_TAKE_FOCUS if the
client doesn't advertise supporting them is finiky. But on the other
hand, the window rules are an advanced feature, so its assumed that the
user knows what they do.

(cherry picked from commit 3663e1ec13)
2024-01-23 22:25:33 +02:00
Vlad Zahorodnii 424ba7d0fc Fix lingering dpms input event filter after cancelling scheduled dpms mode change
The output goes through the following stages when it changes its dpms
mode:

- Output::aboutToTurnOff()]
- some time later, Output::dpmsModeChanged() to indicate that it's off
- Output::dpmsModeChanged() to indicate that it's back on
- Output::wakeUp()

The Output::dpmsModeChanged() signals in the middle are optional. They
may not be emitted after Output::aboutToTurnOff() if the user quickly
cancels the dpms mode transition.

The Workspace should monitor the Output::wakeUp() signal instead.
Alternatively, create the dpms input event filter only after the dpms
mode has changed. While the screen won't be turned back on immediately,
it's still going to produce acceptable visuals. Either solution is fine.
This patch makes the Workspace monitor the wakeUp signal because it
takes fewer lines of code.

BUG: 479659
(cherry picked from commit bbb40a9a84)
2024-01-23 22:25:23 +02:00
Yifan Zhu b0f9cdccdc plugins/fallapart: use InCubic easing
This visual effect corresponds to visible to invisible. So use InCubic
easing for opacity and speed.

(cherry picked from commit adc076322b)
2024-01-23 22:25:13 +02:00
Vlad Zahorodnii e572246ed7 helpers/wayland_wrapper: Bump the buffer size for display_name
The compiler prints the following warning at the moment:

    wl-socket.c:143:9: note: ‘snprintf’ output between 10 and 20 bytes into a destination of size 16

(cherry picked from commit d0a49a6b77)
2024-01-23 22:25:05 +02:00
Xaver Hugl 0fe9e7ac0c move vao from WorkspaceSceneOpenGL to GlxContext
EglContext already had a vao, and it makes more sense for the context to
take care of this than the scene

(cherry picked from commit 8db8dd24bf)
2024-01-23 20:46:08 +01:00
Xaver Hugl e674014d9d outputconfigurationstore: add a fallback for when edid parsing fails
When edid parsing fails, KWin will base output settings on the connector, which
isn't great on its own, but at least works in many cases. When the edid can be
parsed later though, the display settings will reset because now the edid identifier
is used to exclude the old config (in which the latter is missing).
To work around that, this commit adds output identification based on the edid hash,
which is also not ideal, but can be safely matched with in case no output config
with a matching edid identifier exists.

(cherry picked from commit 7e095412aa)
2024-01-23 20:46:04 +01:00
Xaver Hugl 2d08593522 backensd/drm: directly try presentation with changed properties
This optimizes out an unnecessary atomic test - instead of testing the new state and
then attempting to present with that new state, this just directly tries to present the
full state. If that commit fails, the backend just tries again with the safer presentation
mode.

(cherry picked from commit 3cafc20981)
2024-01-23 20:46:01 +01:00
Xaver Hugl d22e6a1977 outputconfigurationstore: take rotation into account for the position
Otherwise outputs may overlap

(cherry picked from commit 7a2d95ddc8)
2024-01-23 20:45:56 +01:00
Vlad Zahorodnii a26d171c78 plugins/qpa: Fix a warning about unused variable
(cherry picked from commit 575ff68e5b)
2024-01-23 21:18:14 +02:00
Vlad Zahorodnii 101a3d075e wayland: Add missing wp_security_context_manager_v1.destroy implementation
(cherry picked from commit 01818f683c)
2024-01-23 21:18:06 +02:00
Yifan Zhu e289c81284 plugins/private/Windowheap: Use InOutCubic easing
All transitions correspond to visible to visible changes, and should
have InOutCubic easing as per HIG.

(cherry picked from commit 8774a0114c)
2024-01-23 21:17:58 +02:00
l10n daemon script 1b4898c2de GIT_SILENT Sync po/docbooks with svn 2024-01-23 03:05:16 +00:00
l10n daemon script 96e85597da SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-01-23 02:58:34 +00:00
Aleix Pol 7ba44593fe screencasting_test: Declaring the metatype is unnecessary now
(cherry picked from commit fd7a67d92e)
2024-01-22 10:44:36 +02:00
Ismael Asensio ab4264a6df kcms/tabbox: Use higher resolution thumbnails for previews
Replace the hardcoded thumbnails which have a low resolution for
current screen standards (~300x200), with higher resolution ones
(~1200x800).

Also set the new 6.0 wallpaper as the fallback desktop thumbnail

BUG: 446765
FIXED-IN: 5.93.0


(cherry picked from commit efde6c8e9e)
2024-01-22 10:41:03 +02:00
Stefan Hoffmeister 264a41cff3 Fix mishandling of output configuration if output monitor EDID is missing
This functionally fixes
* duplication of output configuration in ~/.config/kwinoutputconfig.json
* _wrong_ ouput configuration - in particular the scale - being restored (i.e. scale would never be restored)


(cherry picked from commit d563bc6c86)
2024-01-22 10:39:56 +02:00
Xaver Hugl 219ec5f385 core/renderjournal: improve render time heuristics
Instead of giving the rolling average for render times a special case for
when render time suddenly increases, detect how stable render times are.
If they're volatile and increase a lot, increase the predicated render time
beyond the spike, as more render time spikes are likely to follow.

CCBUG: 477959


(cherry picked from commit 9822e75fbf)
2024-01-22 10:39:03 +02:00
Vlad Zahorodnii 40a9533578 opengl: Correct order of transforms in GLTexturePrivate::updateMatrix
The textureToTransformMatrix should be applied first, otherwise the scale
transform is going to change the winding order of rotations.

In practice though, it shouldn't matter because these matrices are used
to downscale or upscale uv coordinates.


(cherry picked from commit cffacc514d)
2024-01-22 10:37:53 +02:00
Tino Lorenz 5a0f716236 plugins/buttonrebinds: Support rebinding to modifier keys
Allow rebinding buttons to send modifier keys (Control, Alt, Meta,
Shift) without any other key.
This is part of restoring the functionality kcm-wacomtablet had under
X11 for wayland.
CCBUG: 461259

(cherry picked from commit adbf69aab4)
2024-01-22 10:36:46 +02:00
Aki Sakurai a619fc095e Accepting the tablet serial for the xdg_toplevel move/resize function
(cherry picked from commit ab41bcf3ad)
2024-01-22 10:34:56 +02:00
Vlad Zahorodnii 617acb537f opengl: Let OpenGL implementation handle if preprocessor directive
It's less error prone and handles cases like if/elif/else better, etc.

CCBUG: 479279
(cherry picked from commit 7ba7ddfee8)
2024-01-22 10:33:39 +02:00
Vlad Zahorodnii 6c6be75fdf opengl: Fix parsing of the else branch
`it` points to `#else`. We need to move to the next line.

BUG: 479279
(cherry picked from commit 0284610eff)
2024-01-22 10:33:39 +02:00
Vlad Zahorodnii 444b51d95e plugins/colorblindnesscorrection: Ensure that error and correction are fully initialized
vec3() will complain if the vector is not fully initialized. Also if the
type of these variables is changed to vec4 and some miss alpha values,
the glsl compiler will complain again.

(cherry picked from commit 7e8230a596)
2024-01-22 10:33:39 +02:00
Vlad Zahorodnii 22b5634f99 plugins/colorblindnesscorrection: Fix screen becoming white on intel machines
correction.a is uninitialized. It creates problems on intel machines.

BUG: 479749
(cherry picked from commit 4cc94e9462)
2024-01-22 10:33:39 +02:00
l10n daemon script 4e0b7b343e GIT_SILENT Sync po/docbooks with svn 2024-01-22 03:40:08 +00:00
l10n daemon script 0f8893f532 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-01-22 03:36:05 +00:00
l10n daemon script 84c8f4a4e0 GIT_SILENT Sync po/docbooks with svn 2024-01-21 02:55:26 +00:00
Aleix Pol afeee286f5 Revert "screencasting_test: Adapt to KPipeWire change"
This reverts commit e6b8c5318d.

The change in KPipeWire was reverted
2024-01-21 01:53:17 +01:00
l10n daemon script c9b6c5439a GIT_SILENT Sync po/docbooks with svn 2024-01-19 02:59:31 +00:00
Aleix Pol e6b8c5318d screencasting_test: Adapt to KPipeWire change 2024-01-19 01:42:08 +01:00
Xaver Hugl 0050f1443e colors/colordevice: make channel factors linear
The redshift table is in gamma 2.2 encoding and not linear, which means
that it only yields correct results with 1.0 pixel values. It also means
that when it's being applied in linear space in the color management shaders,
the result is quite wrong.

To fix that, this commit makes the channel factors linear and the backend
calculates the nonlinear factors where needed.


(cherry picked from commit 36bec2d941)
2024-01-19 00:15:41 +00:00
Vlad Zahorodnii f1a70c459d plugins/shakecursor: Ensure that cursor is magnificated for at least certain amount of time
If the cursor is magnificated and it's slightly moved so the shake
detector returns std::nullopt, the cursor scale will be set back to 1.0
as soon as possible. This is not ideal. Immediately resetting doesn't
help with locating the cursor.

Also the cursor scale reset delay has been increased to two seconds to
provide more time to see the cursor.


(cherry picked from commit 788c186701)
2024-01-18 19:00:45 +00:00
Vlad Zahorodnii 558c87c359 plugins/screenshot: Use SmoothPixmapTransform when stitching area screenshots
CCBUG: 478426


(cherry picked from commit f6d9b8a0b4)
2024-01-18 17:25:03 +00:00
Vlad Zahorodnii daf7afb68a opengl: Correct transformation order in GLTexture::render()
First, the texture-to-buffer transform has to be applied, then the y
axis should be flipped. Doing it vice versa changes the winding order
of rotation transforms.

Also the screenshot plugin uses incorrect render transform. Since
convertFromGLImage() undoes the render target's transform, the color
space transformation pass should use the same transform, not the
inverted one.

BUG: 479934


(cherry picked from commit f23e0ef05b)
2024-01-18 15:35:30 +00:00
Niccolò Venerandi 94ab3cdb66 kcms/screenedges: Hardcode the Overview as an option in the touchscreen KCM
You can now select "Overview" in the touch KCM to use it as a touch edge gesture again!


(cherry picked from commit 69f8bee0b1)
2024-01-18 14:50:45 +00:00
Xaver Hugl a18c712282 wayland: consider every commit a "content update" for presentation time
The protocol doesn't have a definition of what it means, but damage is a
flawed interpretation. Color management changes are arguably content updates,
so are viewporter changes, so are blur changes and so on.

Also see https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/36
for an upstream discussion of this


(cherry picked from commit d5ca429627)
2024-01-18 13:51:24 +00:00
Vlad Zahorodnii b5884bc721 Rename UnmanagedLayer to OverlayLayer
The UnmanagedLayer layer is used for placing overlay content so
rename it to match how it's used in practice.


(cherry picked from commit da4f4d7e69)
2024-01-18 13:17:04 +00:00
l10n daemon script b57c31a896 GIT_SILENT Sync po/docbooks with svn 2024-01-18 02:54:43 +00:00
Fushan Wen f143003647 plugins/colorblindnesscorrection: remove duplicate definition in Tritanopia shader
(cherry picked from commit f79886765d)
2024-01-17 23:53:03 +00:00
Vlad Zahorodnii 947d8c5e59 Drop window type rule
Practically all code assumes that the window type is static and fixing
it would bring extra complexity, which may not be worth given that there
are window rules to control position, focus, layer, etc.

BUG: 466016
(cherry picked from commit 176ae6e692)
2024-01-18 01:17:03 +02:00
Vlad Zahorodnii 7e6876cc63 Introduce layer window rule
The new window rule allows to overwrite the stack layer. It can be
useful on wayland to force picture-in-picture surfaces (which are
xdg-toplevels at the moment) to be placed above fullscreen windows.
Keep above flag is unsuitable because fullscreen windows are placed
higher "above" windows.

CCBUG: 466016
(cherry picked from commit 5c4acbdddd)
2024-01-18 01:16:55 +02:00
Vlad Zahorodnii 8fb0363432 plugins/shakecursor: Use std::deque to store history
It's more suitable for the task. The ShakeDetector constantly adds new
items at the end of the list and pops old items at the front.


(cherry picked from commit 74a193d383)
2024-01-17 23:04:30 +00:00
Xaver Hugl c297b54c6f backends/drm: ensure commits are done before vblank
If a commit is applied during vblank, the kernel delays it to the next vblank,
for X11 reasons. To ensure that doesn't happen with KWin, read how long
vblank is for the current mode and adjust the safety margin accordingly.

CCBUG: 477959


(cherry picked from commit 7007599b3f)
2024-01-17 21:27:53 +00:00
Yifan Zhu cc75f86e4b window: use QRect to calculate visiblePixels
If QRectF is used, the exact comparison several lines down will fail.


(cherry picked from commit 597753047d)
2024-01-17 18:49:04 +00:00
Yifan Zhu 33077cb81c window: don't use exact comparison for QRectFs
With fractional geometries, we can only guarantee that
nextMoveResizeGeom eventually is within one unit of
currentMoveResizeGeom.

BUG: 479786
FIXED-IN: 6.0


(cherry picked from commit 2dba3d30af)
2024-01-17 18:37:35 +00:00
Nate Graham 74da1f01a5 screenedge: reduce default activation delay
Currently there is a 150ms delay before screen edge actions and
hotcorners are triggered; the cursor has to spend that much time there
before they'll activate. This is good for preventing accidental
activations, but also prevents and delays deliberate activations, which
are likely to be more annoying for the user.

To alleviate this, the delay is reduced to 75ms, which still prevents
most of the accidental activations in my testing, while making
deliberate activations faster and easier.


(cherry picked from commit e963ae09cc)
2024-01-17 15:28:09 +00:00
David Edmundson 37d2a79143 plugins/screencast: Don't recreate the core every stream
Loading pipewire is noticably slow with libpipewire loading a bunch of
internals.

Change from a singleton pattern to being managed by the
ScreencastManager

This is still unloaded if the plugin is explicitly stopped.


(cherry picked from commit 42c61dc6a3)
2024-01-17 14:26:54 +00:00
Vlad Zahorodnii 0e62379248 backends/drm: Provide a way to force color management
It can be handy if kwin runs on a setup not capable of HDR or without an
ICC profile. The output management protocol has not been touched because
the long term goal is to have color management enabled by default. This
is rather for testing purposes.


(cherry picked from commit 9c2f12f98f)
2024-01-17 11:43:14 +00:00
Xaver Hugl 6e8f56f713 plugins/fullscreen: retarget the animation instead of restarting it
BUG: 474488
FIXED-IN: 6.0


(cherry picked from commit e9db9cd7d3)
2024-01-17 11:03:04 +00:00
Jie Liu 8c318194eb Only precompute the geometry restore when m_interactiveMoveResize.initialQuickTileMode is None
If the user wants to move a tiled window but changes their mind and tiles the window back to the previous
position, quickTileGeometryRestore() will return an error value beacause m_electricMaximizing is true and
the m_electricGeometryRestore is the same as the geometry of the window in the last tiled mode.

Now the geometry restore of the tiled window is true when starting interactive move, so we no longer need
to precompute it.

Changes for testQuickTilingPointerMove:

We need to attach a new image after the tiling window, so that updateElectricGeometryRestore can obtain
the same framegeometry as the framegeometry obtained during actual runtime.

Now testQuickTilingPointerMove can detect the error:If the user wants to move a tiled window but changes
their mind and tiles the window back to the previous position, quickTileGeometryRestore() will return an
error value beacause m_electricMaximizing is true and the m_electricGeometryRestore is the same as the
geometry of the window in the last tiled mode.

Signed-off-by: Jie Liu <liujie01@kylinos.cn>


(cherry picked from commit 1a1eaee13f)
2024-01-17 09:48:55 +00:00
l10n daemon script f5e552c07b GIT_SILENT Sync po/docbooks with svn 2024-01-17 02:59:14 +00:00
Xaver Hugl e20c953b8a wayland/surface: store subsurface transaction in a unique_ptr
(cherry picked from commit fd2e77317f)
2024-01-17 01:00:51 +00:00
Xaver Hugl 3569eaab15 backends/drm: apply the ICC profile without premultiplication
As a side effect, this ensures that when alpha is zero, rgb is zero as well.
This is needed because the ICC profile may contain transformations where zero
brightness gets mapped to a non-zero value.

BUG: 479380


(cherry picked from commit 6ab8f179a7)
2024-01-16 19:05:25 +00:00
Xaver Hugl 12c3197ca6 opengl/glshadermanager: add missing newline
BUG: 479279


(cherry picked from commit ed339de953)
2024-01-16 13:55:02 +00:00
David Redondo 228ef989b4 Make sure window thumbnails and Qt Quick resources are destroyed properly
Drops the doneCurrent as it was preventing proper cleanUp
because no context was current when textures were deleted.
Also avoid manipulating the context when Qt has the current
one, as various Qt classes have guards around their cleanup
handlers which rely on a current Qt context.
Despite the comment the order of render control and view destruction
needs to be switched as the QQuickWindow destructor calls into
the render control to notify if of window destruction.
BUG:478770
BUG:479846
FIXED-IN:6.0


(cherry picked from commit 203c4998bc)
2024-01-16 13:34:11 +00:00
l10n daemon script 3a3ea5d23a GIT_SILENT Sync po/docbooks with svn 2024-01-16 03:00:00 +00:00
Xaver Hugl c115b793a9 backends/drm: make icc profiles work with OpenGL ES
OpenGL doesn't support 1D textures, and has some special requirements in
the shaders, like no implicit conversion from int to float


(cherry picked from commit 4433fb48dc)
2024-01-15 19:15:25 +00:00
Joshua Goins 5b557a6ada Add missing find_dependency for epoxy
This is required by the KWin target, but was missing a find_dependency
call. If any downstream project tried to link to the KWin::kwin target,
it would fail because epoxy couldn't be found.


(cherry picked from commit c3d3d83b56)
2024-01-15 19:00:48 +00:00
David Edmundson 49ced6dcdd screencasting: Resume streams after buffer renegotiation
When we start renegotiation the stream is pasused. This is not restarted
when the format has been renegotiated.

This occurred when a streaming window was resized


(cherry picked from commit 2e6619f3d0)
2024-01-15 18:20:47 +00:00
Vlad Zahorodnii 3f5f1ce9ba scene: Remove warning message about older presentation timestamps
Outputs present frames at different pace, some can present new content
later, some sooner. If the scene gets a slightly older presentation
timestamp, it's okay.

(cherry picked from commit f665dda192)
2024-01-15 19:57:33 +02:00
Vlad Zahorodnii b813b9879a Break show desktop mode if a new window is added
BUG: 479424
(cherry picked from commit af39179446)
2024-01-15 19:57:21 +02:00
Vlad Zahorodnii 484e7c94e6 autotests: Add a test to verify that show desktop mode quits after activating a window
(cherry picked from commit 359f36d6ab)
2024-01-15 19:57:10 +02:00
Vlad Zahorodnii 05de026fd2 backends/drm: Fix a crash in VirtualEglGbmLayer::texture()
If nothing has been rendered yet, m_currentSlot will be null.

BUG: 475296
BUG: 479558
(cherry picked from commit 3b67edf20d)
2024-01-15 19:57:01 +02:00
Vlad Zahorodnii f05f1a29d0 effect: Remove const refs in EffectFrame
It's a micro optimization.

(cherry picked from commit 72c391ebc8)
2024-01-15 19:56:51 +02:00
Vlad Zahorodnii 9a09ebe673 plugins/mouseclick: Fix glitches
Effect frame geometry is not properly synchronized with the quick
scene geometry.

BUG: 477892
(cherry picked from commit 09f97c8037)
2024-01-15 19:56:42 +02:00
Vlad Zahorodnii b62d212a58 Fix cleanup of forced window shortcuts
XdgToplevelWindow doesn't call finishWindowRules(). It creates a problem
for Workspace::removeWindow() because it calls setShortcut() to release
the window shortcut.

While one way to fix the bug would be to add a finishWindowRules() call
in XdgToplevelWindow, it would perhaps be not the best one because it
would change the appearance of decoration when the window is closed.

Instead, this change makes the workspace release the shortcut when the
window is closed. It has a couple of advantages: the appearance of the
decoration won't change, shortcut cleanup is better encapsulated.

BUG: 478647


(cherry picked from commit b98607c689)
2024-01-15 14:41:54 +00:00
Vlad Zahorodnii 3a37f8dbb2 scripting: Fix a crash in useGlThumbnails() when compositing is off
BUG: 479791


(cherry picked from commit 4f8c941bff)
2024-01-15 14:24:33 +00:00
Vlad Zahorodnii fc7a92096b plugins: Hide "Drag Down to Close" when using a pointing device
BUG: 479802


(cherry picked from commit 2d71d7cada)
2024-01-15 14:07:27 +00:00
Vlad Zahorodnii e87dcd4cd9 plugins/shakecursor: Ignore motion events with at least one pressed button
It reduces the chances of false triggering the plugin, for example if
the user presses a mouse button and chaotically moves the pointer
because they navigate in 3D space and what not.


(cherry picked from commit 0ddcc85f68)
2024-01-15 09:39:00 +00:00
Yifan Zhu f0387d2f85 scene/cursordelegate_opengl: undo ortho() flip y
Flip y axis before and after applying renderTarget.transform to undo
ortho() flipping the y axis. Otherwise the cursor is invisible on
rotated screen.

BUG: 479764
FIXED-IN: 6.0


(cherry picked from commit 1f74e9a5c5)
2024-01-15 08:19:32 +00:00
Weng Xuetian 1ac4a2498d
inputmethod: Ensure InputPanelV1Window is always within the screen
Currently when input panel is using overlay mode and the cursor rectangle
is below or above the screen area, the input panel may be placed off the
screen. The change ensure it is always placed within the screen area
using similar math like xdg_popup's slide_y constrain.

(cherry picked from commit 1fd5a6555e)
2024-01-14 09:48:35 -08:00
l10n daemon script ef7664a647 GIT_SILENT Sync po/docbooks with svn 2024-01-14 03:45:34 +00:00
Yifan Zhu ffb13a1cd8 kcms/options: port to KCModule methods
Remove code that duplicates functionality of KCModule.

Previously the various config items were not fully ported to using
KCModule methods, and kept custom versions of isSaveNeeded and
isDefault. When called from KWinOptions::updateUnmanagedState, these
methods incorrectly reports that there are still changes to be saved.
This patch set ports all configs in window behavior to methods provided
by KCModule, solving the problem.

BUG: 477940


(cherry picked from commit faab23e914)
2024-01-13 18:32:47 +00:00
Xaver Hugl 97df214487 opengl/glshader: use gamma 2.2 for sRGB render targets
The sRGB global colordescription uses gamma 2.2, so this function also has to do
it, or the shader will end up doing a colorspace conversion where there shouldn't
be one.
This also meams that HDR content will be wrongly encoded, but fixing that while
also dealing with sRGB content correctly requires a lot more invasive changes, in
KWin and in the screenshot and screencast APIs.

BUG: 478967


(cherry picked from commit c1e09f65c9)
2024-01-13 17:28:01 +00:00
Ismael Asensio eca4049ef6 kcms/tabbox: Fix preview for show desktop option
Use the correct config option to decide whether to show the desktop
thumbnail on preview. "DesktopMode" refers to filter by desktop.

(cherry picked from commit 20bee00681)
2024-01-13 17:10:55 +01:00
Ismael Asensio b08547968d kcms/tabbox: Mock-up closeable role for previews
Fixes a qml warning and wrong representation when trying to
access this model property

(cherry picked from commit 3353ed87ef)
2024-01-13 16:07:30 +01:00
Ismael Asensio 7bd9fe6827 kcms/tabbox: Add mock-up object for KWin.DesktopBackground
The window switcher previews don't use the actual quick components
the KWin plugin provides, but mock-up objects of those.

Add a mock-up component for `KWin.DesktopBackground`, which is just
a thumbnail of the desktop default image but stretched to cover the
whole parent size.

This fixes the previews for flip&cover window switchers.

BUG: 479552
FIXED-IN: 5.93.0

(cherry picked from commit 3572e6f0e7)
2024-01-13 16:07:25 +01:00
Alexander Lohnau 3fa088af00 Remove unneeded includes from headers and cpp files
(cherry picked from commit 7d1db53f6b)
2024-01-13 11:26:36 +00:00
Ismael Asensio 0dd9f0a31f kcms/tabbox: Fix tabbox preview not getting dismissed
On X11 we would dismiss the tabbox preview when clicking outside
of the switcher. This doesn't work on Wayland because the popup
cannot globally grab the mouse and doesn't get notified.

Use `QEvent::FocusOut` instead which works on both platforms.

We also get rid of a warning message.

BUG: 374971
FIXED-IN: 5.93.0


(cherry picked from commit b08a4a1896)
2024-01-12 23:07:35 +00:00
Xaver Hugl df4657c963 opengl/glshader: print the shader source if compilation fails
The shader log is almost entirely useless without the source

CCBUG: 479279


(cherry picked from commit 427ce522a2)
2024-01-12 21:17:53 +00:00
Xaver Hugl 4b4852463f backends/drm: wait for the pageflip to be done with the condition variable
...instead of busy looping, which is causing several percent CPU usage in some cases

BUG: 479126


(cherry picked from commit d4a3d6689e)
2024-01-12 20:56:05 +00:00
Xaver Hugl 4a674e6726 backends/drm: don't call QThread::currentThread in a loop
We're always in the same thread, so there's no need to check it


(cherry picked from commit 56fc8f83ac)
2024-01-12 20:40:15 +00:00
Vlad Zahorodnii fc4a6ab4ff wayland: Fix handling of unminimization for not fully initialized xdg-toplevels
If setMinimized(false) is called for a not fully initialized
XdgToplevelWindow, we don't want to emit the windowShown signal.

BUG: 479234


(cherry picked from commit c59f385996)
2024-01-12 13:09:48 +00:00
Vlad Zahorodnii 37b6092aef Fix software cursor stucking on old monitor
When the software cursor leaves an output, its RenderLayer is marked
as invisible and the area corresponding to the layer is scheduled to
be repainted in the output layer. However, the composite function only
checks whether the root RenderLayer needs a repaint, not the output
layer too.

BUG: 479668


(cherry picked from commit 1c15a6dd88)
2024-01-12 12:39:55 +00:00
Xaver Hugl e7870f0841 input: remove the terminate server shortcut
Accidentally pressing any shortcut shouldn't take down the whole session.
If KWin isn't reponsive but can still process shortcuts for some reason,
you can still switch to a different virtual terminal to explicitly kill
it from there


(cherry picked from commit 260bc0a61d)
2024-01-12 12:27:05 +00:00
Xaver Hugl d1d1b52bed backends/drm: don't allow implicit modifiers for multi gpu transfers
As we translate DRM_FORMAT_MOD_LINEAR to implicit modifiers + linear flag, the
egl import path should still work without implicit modifiers too.

BUG: 478921


(cherry picked from commit 451b878bb9)
2024-01-12 12:17:18 +00:00
Xaver Hugl d02cf12f13 opengl: don't crash in GLTexture::toImage with OpenGL ES
OpenGL ES 2 doesn't support glGetTexImage


(cherry picked from commit bc58d13ee8)
2024-01-12 11:48:00 +00:00
Jin Liu a5437f9af7 Update keyboard focus when exiting from a keyboard-grabbing effect
BUG: 479628


(cherry picked from commit 18ba622c40)
2024-01-12 11:32:01 +00:00
Vlad Zahorodnii 88d19feec4 Fix evaluating window rules for closed windows
gedit changes its caption from "Untitled document - gedit" to "gedit"
when it closes. This schedules Window::evaluateWindowRules() to be
called when the window is already marked as deleted. kwin then crashes.

In order to prevent that, a direct connection can be used instead. But
then the caption must be initialized extra carefully because if the
window rule changes the window type, "<N>" can be lost.

(cherry picked from commit bc30ca64dc)
2024-01-12 11:14:48 +00:00
Vlad Zahorodnii fe5e3a4ce2 Add Window::captionNormalChanged()
This signal notifies when the client specified caption changes, not when
kwin changes it because it changed the suffix, etc.

(cherry picked from commit 4ab504c994)
2024-01-12 11:14:48 +00:00
Vlad Zahorodnii a7f1c284a0 scene: Fix alpha channel of offscreen cursor texture
When running with 10bpc, the alpha channel has only 2 bits for it, which
is too low for the cursor.

BUG: 479637


(cherry picked from commit 68f1570d45)
2024-01-12 11:01:50 +00:00
Jie Liu 2292ad8bd0 update keyboard focus when window switcher is closed.
BUG:477885

Signed-off-by: Jie Liu <liujie01@kylinos.cn>


(cherry picked from commit e427ad73aa)
2024-01-12 10:37:52 +00:00
Vlad Zahorodnii 0da6354bce scene: Fix software cursor clip region
The dirty region is in the output local coordinate space.

BUG: 479583
(cherry picked from commit 36222adb25)
2024-01-12 10:20:42 +00:00
Vlad Zahorodnii e3e4547392 core: Add Output::rectF
(cherry picked from commit 571e4026ac)
2024-01-12 10:20:42 +00:00
Vlad Zahorodnii 680a759852 core: Rename Output::fractionalGeometry()
In Qt, if an overloaded function returns QRectF/QSizeF/QPointF, it
usually has F suffix. Do the same in kwin for the consistency sake.

(cherry picked from commit c314705d53)
2024-01-12 10:20:42 +00:00
Vlad Zahorodnii fce26936d0 effect: Install xcb.h
BUG: 479584


(cherry picked from commit 3a5ba58a45)
2024-01-12 10:06:59 +00:00
Vlad Zahorodnii 62ac1f86d1 xwayland: Make xinerama index -> Output mapping more robust with fractional scaling
xdg-output-v1 uses the fractional geometry, so also use it when mapping
a xinerama screen index to an Output.


(cherry picked from commit 66b1462cf8)
2024-01-12 09:55:35 +00:00
Vlad Zahorodnii 0b1759076c xwayland: Fix primary output identification
Output::geometry() returns a logical geometry, while the crtc rect is in
the device pixels.


(cherry picked from commit 3f1e57de1c)
2024-01-12 09:20:42 +00:00
Xaver Hugl bce1047080 effect/offscreenquickview: always use GL_RGBA8
GL_RGB8 isn't supported by OpenGL ES

BUG: 479055


(cherry picked from commit a05a6d8727)
2024-01-10 22:47:04 +00:00
l10n daemon script 3298ae3405 GIT_SILENT Sync po/docbooks with svn 2024-01-10 22:42:15 +00:00
Jonathan Esk-Riddell 9a73beecdd Update version number for 5.92.90
GIT_SILENT
2024-01-10 14:17:35 +00:00
1627 changed files with 65814 additions and 81113 deletions

View File

@ -81,6 +81,3 @@ AllowShortLambdasOnASingleLine: Empty
# We do not want clang-format to put all arguments on a new line
AllowAllArgumentsOnNextLine: false
# Indent lambdas to the start of the line, not to the start of the lambda
LambdaBodyIndentation: OuterScope

1
.gitignore vendored
View File

@ -25,4 +25,3 @@ CMakeLists.txt.user*
.idea
/cmake-build*
.cache
.directory

View File

@ -7,8 +7,8 @@ include:
- /gitlab-templates/linux-qt6.yml
- /gitlab-templates/freebsd-qt6.yml
suse_tumbleweed_qt67_reduced_featureset:
extends: suse_tumbleweed_qt67
suse_tumbleweed_qt66_reduced_featureset:
extends: suse_tumbleweed_qt66
script:
- git config --global --add safe.directory $CI_PROJECT_DIR
- python3 -u ci-utilities/run-ci-build.py --project $CI_PROJECT_NAME --branch $CI_COMMIT_REF_NAME --platform Linux --extra-cmake-args="-DKWIN_BUILD_KCMS=OFF -DKWIN_BUILD_SCREENLOCKER=OFF -DKWIN_BUILD_TABBOX=OFF -DKWIN_BUILD_ACTIVITIES=OFF -DKWIN_BUILD_RUNNERS=OFF -DKWIN_BUILD_NOTIFICATIONS=OFF -DKWIN_BUILD_GLOBALSHORTCUTS=OFF -DKWIN_BUILD_X11=OFF -DKWIN_BUILD_EIS=OFF" --skip-publishing
- python3 -u ci-utilities/run-ci-build.py --project $CI_PROJECT_NAME --branch $CI_COMMIT_REF_NAME --platform Linux --extra-cmake-args="-DKWIN_BUILD_KCMS=OFF -DKWIN_BUILD_SCREENLOCKER=OFF -DKWIN_BUILD_TABBOX=OFF -DKWIN_BUILD_ACTIVITIES=OFF -DKWIN_BUILD_RUNNERS=OFF -DKWIN_BUILD_NOTIFICATIONS=OFF -DKWIN_BUILD_GLOBALSHORTCUTS=OFF" --skip-publishing

View File

@ -1,15 +1,14 @@
cmake_minimum_required(VERSION 3.16)
set(PROJECT_VERSION "6.1.80") # Handled by release scripts
set(PROJECT_VERSION "6.0.3") # Handled by release scripts
project(KWin VERSION ${PROJECT_VERSION})
set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(PROJECT_DEP_VERSION "6.1.1")
set(QT_MIN_VERSION "6.6.0")
set(KF6_MIN_VERSION "6.2.0")
set(KF6_MIN_VERSION "6.0.0")
set(KDE_COMPILERSETTINGS_LEVEL "5.82")
find_package(ECM ${KF6_MIN_VERSION} REQUIRED NO_MODULE)
@ -48,9 +47,6 @@ option(KWIN_BUILD_KCMS "Enable building of KWin configuration modules." ON)
option(KWIN_BUILD_NOTIFICATIONS "Enable building of KWin with knotifications support" ON)
option(KWIN_BUILD_SCREENLOCKER "Enable building of KWin lockscreen functionality" ON)
option(KWIN_BUILD_TABBOX "Enable building of KWin Tabbox functionality" ON)
option(KWIN_BUILD_X11 "Enable building kwin_x11 and Xwayland support" ON)
option(KWIN_BUILD_GLOBALSHORTCUTS "Enable building of KWin with global shortcuts support" ON)
option(KWIN_BUILD_RUNNERS "Enable building of KWin with krunner support" ON)
find_package(Qt6 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
Concurrent
@ -75,13 +71,12 @@ if (NOT Qt6Test_FOUND)
endif()
if (BUILD_TESTING)
find_package(KPipeWire)
pkg_check_modules(PipeWire IMPORTED_TARGET libpipewire-0.3 REQUIRED)
endif()
# required frameworks by Core
find_package(KF6 ${KF6_MIN_VERSION} REQUIRED COMPONENTS
Auth
ColorScheme
Config
ConfigWidgets
CoreAddons
@ -114,14 +109,14 @@ set_package_properties(Threads PROPERTIES
TYPE REQUIRED
)
find_package(KWayland ${PROJECT_DEP_VERSION} CONFIG)
find_package(KWayland ${PROJECT_VERSION} CONFIG)
set_package_properties(KWayland PROPERTIES
PURPOSE "Required to build wayland platform plugin and tests"
TYPE REQUIRED
)
# optional frameworks
find_package(PlasmaActivities ${PROJECT_DEP_VERSION} CONFIG)
find_package(PlasmaActivities ${PROJECT_VERSION} CONFIG)
set_package_properties(PlasmaActivities PROPERTIES
PURPOSE "Enable building of KWin with kactivities support"
TYPE OPTIONAL
@ -135,20 +130,20 @@ set_package_properties(KF6DocTools PROPERTIES
)
add_feature_info("KF6DocTools" KF6DocTools_FOUND "Enable building documentation")
find_package(KF6Kirigami ${KF6_MIN_VERSION} CONFIG)
set_package_properties(KF6Kirigami PROPERTIES
find_package(KF6Kirigami2 ${KF6_MIN_VERSION} CONFIG)
set_package_properties(KF6Kirigami2 PROPERTIES
DESCRIPTION "A QtQuick based components set"
PURPOSE "Required at runtime for several QML effects"
TYPE RUNTIME
)
find_package(Plasma ${PROJECT_DEP_VERSION} CONFIG)
find_package(Plasma ${PROJECT_VERSION} CONFIG)
set_package_properties(Plasma PROPERTIES
DESCRIPTION "A QtQuick based components set"
PURPOSE "Required at runtime for several QML effects"
TYPE RUNTIME
)
find_package(KDecoration2 ${PROJECT_DEP_VERSION} CONFIG)
find_package(KDecoration2 ${PROJECT_VERSION} CONFIG)
set_package_properties(KDecoration2 PROPERTIES
TYPE REQUIRED
PURPOSE "Required for server side decoration support"
@ -192,25 +187,33 @@ if (epoxy_HAS_GLX)
endif()
endif()
check_cxx_source_compiles("
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
int main() {
const int size = 10;
int fd = memfd_create(\"test\", MFD_CLOEXEC | MFD_ALLOW_SEALING);
ftruncate(fd, size);
fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL);
mmap(nullptr, size, PROT_WRITE, MAP_SHARED, fd, 0);
}" HAVE_MEMFD)
find_package(Wayland 1.22)
set_package_properties(Wayland PROPERTIES
TYPE REQUIRED
PURPOSE "Required for building KWin with Wayland support"
)
if (Wayland_VERSION VERSION_GREATER_EQUAL 1.23)
set(HAVE_WL_DISPLAY_SET_DEFAULT_MAX_BUFFER_SIZE 1)
else()
set(HAVE_WL_DISPLAY_SET_DEFAULT_MAX_BUFFER_SIZE 0)
endif()
find_package(WaylandProtocols 1.34)
find_package(WaylandProtocols 1.32)
set_package_properties(WaylandProtocols PROPERTIES
TYPE REQUIRED
PURPOSE "Collection of Wayland protocols that add functionality not available in the Wayland core protocol"
URL "https://gitlab.freedesktop.org/wayland/wayland-protocols/"
)
find_package(PlasmaWaylandProtocols 1.13.0 CONFIG)
find_package(PlasmaWaylandProtocols 1.9.0 CONFIG)
set_package_properties(PlasmaWaylandProtocols PROPERTIES
TYPE REQUIRED
PURPOSE "Collection of Plasma-specific Wayland protocols"
@ -228,67 +231,12 @@ else()
set(HAVE_XKBCOMMON_NO_SECURE_GETENV 0)
endif()
if (KWIN_BUILD_X11)
pkg_check_modules(XKBX11 IMPORTED_TARGET xkbcommon-x11 REQUIRED)
add_feature_info(XKBX11 XKBX11_FOUND "Required for handling keyboard events in X11 backend")
# All the required XCB components
find_package(XCB 1.10 REQUIRED COMPONENTS
COMPOSITE
CURSOR
DAMAGE
DRI3
GLX
ICCCM
IMAGE
KEYSYMS
PRESENT
RANDR
RENDER
SHAPE
SHM
SYNC
XCB
XFIXES
XKB
XINERAMA
XINPUT
)
set_package_properties(XCB PROPERTIES TYPE REQUIRED)
find_package(X11_XCB)
set_package_properties(X11_XCB PROPERTIES
PURPOSE "Required for building X11 windowed backend of kwin_wayland"
TYPE OPTIONAL
)
find_package(Xwayland)
set_package_properties(Xwayland PROPERTIES
URL "https://x.org"
DESCRIPTION "Xwayland X server"
TYPE RUNTIME
PURPOSE "Needed for running kwin_wayland"
)
set(HAVE_XWAYLAND_LISTENFD ${Xwayland_HAVE_LISTENFD})
set(HAVE_XWAYLAND_ENABLE_EI_PORTAL ${Xwayland_HAVE_ENABLE_EI_PORTAL})
set(HAVE_GLX ${epoxy_HAS_GLX})
get_target_property(QT_DISABLED_FEATURES Qt6::Gui QT_DISABLED_PUBLIC_FEATURES)
if("xcb_glx_plugin" IN_LIST QT_DISABLED_FEATURES)
message(STATUS "Disable GLX because Qt6::Gui was built without xcb_glx_plugin")
set(HAVE_GLX false)
endif()
# for kwin internal things
set(HAVE_X11_XCB ${X11_XCB_FOUND})
endif()
pkg_check_modules(XKBX11 IMPORTED_TARGET xkbcommon-x11 REQUIRED)
add_feature_info(XKBX11 XKBX11_FOUND "Required for handling keyboard events in X11 backend")
find_package(Libinput 1.19)
set_package_properties(Libinput PROPERTIES TYPE REQUIRED PURPOSE "Required for input handling on Wayland.")
find_package(Libeis-1.0)
set_package_properties(Libeis PROPERTIES TYPE OPTIONAL PURPOSE "Required for emulated input handling.")
find_package(UDev)
set_package_properties(UDev PROPERTIES
URL "https://www.freedesktop.org/wiki/Software/systemd/"
@ -297,7 +245,7 @@ set_package_properties(UDev PROPERTIES
PURPOSE "Required for input handling on Wayland."
)
find_package(Libdrm 2.4.116)
find_package(Libdrm 2.4.112)
set_package_properties(Libdrm PROPERTIES TYPE REQUIRED PURPOSE "Required for drm output on Wayland.")
find_package(gbm)
@ -333,6 +281,35 @@ set_package_properties(lcms2 PROPERTIES
PURPOSE "Required for the color management system"
)
# All the required XCB components
find_package(XCB 1.10 REQUIRED COMPONENTS
COMPOSITE
CURSOR
DAMAGE
DRI3
GLX
ICCCM
IMAGE
KEYSYMS
PRESENT
RANDR
RENDER
SHAPE
SHM
SYNC
XCB
XFIXES
XKB
XINERAMA
)
set_package_properties(XCB PROPERTIES TYPE REQUIRED)
find_package(X11_XCB)
set_package_properties(X11_XCB PROPERTIES
PURPOSE "Required for building X11 windowed backend of kwin_wayland"
TYPE OPTIONAL
)
find_package(Freetype)
set_package_properties(Freetype PROPERTIES
DESCRIPTION "A font rendering engine"
@ -346,6 +323,15 @@ set_package_properties(Fontconfig PROPERTIES
PURPOSE "Needed for KWin's QPA plugin."
)
find_package(Xwayland)
set_package_properties(Xwayland PROPERTIES
URL "https://x.org"
DESCRIPTION "Xwayland X server"
TYPE RUNTIME
PURPOSE "Needed for running kwin_wayland"
)
set(HAVE_XWAYLAND_LISTENFD ${Xwayland_HAVE_LISTENFD})
find_package(Libcap)
set_package_properties(Libcap PROPERTIES
TYPE OPTIONAL
@ -369,19 +355,45 @@ set_package_properties(QAccessibilityClient6 PROPERTIES
)
set(HAVE_ACCESSIBILITY ${QAccessibilityClient6_FOUND})
pkg_check_modules(libsystemd IMPORTED_TARGET libsystemd)
add_feature_info(libsystemd libsystemd_FOUND "Required for setting up the service watchdog")
option(KWIN_BUILD_GLOBALSHORTCUTS "Enable building of KWin with global shortcuts support" ON)
if(KWIN_BUILD_GLOBALSHORTCUTS)
find_package(KGlobalAccelD REQUIRED)
endif()
pkg_check_modules(libdisplayinfo IMPORTED_TARGET display-info)
if (NOT libdisplayinfo_FOUND)
pkg_check_modules(libdisplayinfo REQUIRED IMPORTED_TARGET libdisplay-info)
endif()
add_feature_info(libdisplayinfo libdisplayinfo_FOUND "EDID and DisplayID library: https://gitlab.freedesktop.org/emersion/libdisplay-info")
ecm_find_qmlmodule(QtQuick 2.3)
ecm_find_qmlmodule(QtQuick.Controls 2.15)
ecm_find_qmlmodule(QtQuick.Layouts 1.3)
ecm_find_qmlmodule(QtQuick.Window 2.1)
ecm_find_qmlmodule(QtMultimedia 5.0)
ecm_find_qmlmodule(org.kde.kquickcontrolsaddons 2.0)
ecm_find_qmlmodule(org.kde.plasma.core 2.0)
ecm_find_qmlmodule(org.kde.plasma.components 2.0)
########### configure tests ###############
cmake_dependent_option(KWIN_BUILD_ACTIVITIES "Enable building of KWin with kactivities support" ON "PlasmaActivities_FOUND" OFF)
option(KWIN_BUILD_RUNNERS "Enable building of KWin with krunner support" ON)
set(HAVE_GLX ${epoxy_HAS_GLX})
get_target_property(QT_DISABLED_FEATURES Qt6::Gui QT_DISABLED_PUBLIC_FEATURES)
if("xcb_glx_plugin" IN_LIST QT_DISABLED_FEATURES)
message(STATUS "Disable GLX because Qt6::Gui was built without xcb_glx_plugin")
set(HAVE_GLX false)
endif()
# for kwin internal things
set(HAVE_X11_XCB ${X11_XCB_FOUND})
check_symbol_exists(SCHED_RESET_ON_FORK "sched.h" HAVE_SCHED_RESET_ON_FORK)
add_feature_info("SCHED_RESET_ON_FORK"
HAVE_SCHED_RESET_ON_FORK
"Required for running kwin_wayland with real-time scheduling")
pkg_check_modules(PipeWire IMPORTED_TARGET libpipewire-0.3>=0.3.29)
add_feature_info(PipeWire PipeWire_FOUND "Required for Wayland screencasting")
@ -397,17 +409,7 @@ if (KWIN_BUILD_SCREENLOCKER)
)
endif()
ecm_find_qmlmodule(QtQuick 2.3)
ecm_find_qmlmodule(QtQuick.Controls 2.15)
ecm_find_qmlmodule(QtQuick.Layouts 1.3)
ecm_find_qmlmodule(QtQuick.Window 2.1)
ecm_find_qmlmodule(QtMultimedia 5.0)
ecm_find_qmlmodule(org.kde.kquickcontrolsaddons 2.0)
ecm_find_qmlmodule(org.kde.plasma.core 2.0)
ecm_find_qmlmodule(org.kde.plasma.components 2.0)
cmake_dependent_option(KWIN_BUILD_ACTIVITIES "Enable building of KWin with kactivities support" ON "PlasmaActivities_FOUND" OFF)
cmake_dependent_option(KWIN_BUILD_EIS "Enable building KWin with libeis support" ON "Libeis-1.0_FOUND" OFF)
########### global ###############
include_directories(BEFORE
${CMAKE_CURRENT_BINARY_DIR}/src/wayland
@ -415,24 +417,6 @@ include_directories(BEFORE
${CMAKE_CURRENT_SOURCE_DIR}/src
)
check_symbol_exists(SCHED_RESET_ON_FORK "sched.h" HAVE_SCHED_RESET_ON_FORK)
add_feature_info("SCHED_RESET_ON_FORK"
HAVE_SCHED_RESET_ON_FORK
"Required for running kwin_wayland with real-time scheduling")
check_cxx_source_compiles("
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
int main() {
const int size = 10;
int fd = memfd_create(\"test\", MFD_CLOEXEC | MFD_ALLOW_SEALING);
ftruncate(fd, size);
fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL);
mmap(nullptr, size, PROT_WRITE, MAP_SHARED, fd, 0);
}" HAVE_MEMFD)
check_cxx_compiler_flag(-Wno-unused-parameter COMPILER_UNUSED_PARAMETER_SUPPORTED)
if (COMPILER_UNUSED_PARAMETER_SUPPORTED)
add_compile_options(-Wno-unused-parameter)

View File

@ -27,9 +27,9 @@ The Breeze decorations theme is not located in the KWin repository, and is in fa
### Tab Switcher
The default visual appearance of the tab switcher is located in `src/tabbox/switchers`.
The default visual appearance of the tab switcher is not located in the KWin repository, and is in fact part of [Plasma Workspace](https://invent.kde.org/plasma/plasma-workspace), located at `lookandfeel/contents/windowswitcher`.
Other window switchers usually shipped by default are located in [Plasma Addons](https://invent.kde.org/plasma/kdeplasma-addons), located in the `kwin/windowswitchers` directory.
Other window switchers usually shipped by default are located in [Plasma Addons](https://invent.kde.org/plasma/kdeplasma-addons), located in the `windowswitchers` directory.
### Window Management
@ -53,7 +53,7 @@ Other scripting stuff is located in `src/scripting`.
KWin's coding conventions are located [here](doc/coding-conventions.md).
KWin additionally follows [KDE's Frameworks Coding Style](https://community.kde.org/Policies/Frameworks_Coding_Style).
KWin additionally follows [KDE's Frameworks Coding Style]((https://techbase.kde.org/Policies/Frameworks_Coding_Style)).
### Commits

View File

@ -45,97 +45,82 @@ ecm_mark_as_test(testVirtualDesktops)
########################################################
# Test ClientMachine
########################################################
if(KWIN_BUILD_X11)
set(testClientMachine_SRCS
../src/client_machine.cpp
test_client_machine.cpp
xcb_scaling_mock.cpp
)
add_executable(testClientMachine ${testClientMachine_SRCS})
set_target_properties(testClientMachine PROPERTIES COMPILE_DEFINITIONS "NO_NONE_WINDOW")
set(testClientMachine_SRCS
../src/client_machine.cpp
test_client_machine.cpp
xcb_scaling_mock.cpp
)
add_executable(testClientMachine ${testClientMachine_SRCS})
set_target_properties(testClientMachine PROPERTIES COMPILE_DEFINITIONS "NO_NONE_WINDOW")
target_link_libraries(testClientMachine
Qt::Concurrent
Qt::GuiPrivate
Qt::Test
Qt::Widgets
target_link_libraries(testClientMachine
Qt::Concurrent
Qt::GuiPrivate
Qt::Test
Qt::Widgets
KF6::ConfigCore
KF6::WindowSystem
KF6::ConfigCore
KF6::WindowSystem
XCB::XCB
XCB::XFIXES
XCB::XCB
XCB::XFIXES
${X11_X11_LIB} # to make jenkins happy
)
add_test(NAME kwin-testClientMachine COMMAND testClientMachine)
ecm_mark_as_test(testClientMachine)
${X11_X11_LIB} # to make jenkins happy
)
add_test(NAME kwin-testClientMachine COMMAND testClientMachine)
ecm_mark_as_test(testClientMachine)
########################################################
# Test XcbWrapper
########################################################
add_executable(testXcbWrapper test_xcb_wrapper.cpp xcb_scaling_mock.cpp)
########################################################
# Test XcbWrapper
########################################################
add_executable(testXcbWrapper test_xcb_wrapper.cpp xcb_scaling_mock.cpp)
target_link_libraries(testXcbWrapper
Qt::GuiPrivate
Qt::Test
Qt::Widgets
target_link_libraries(testXcbWrapper
Qt::GuiPrivate
Qt::Test
Qt::Widgets
KF6::ConfigCore
KF6::WindowSystem
KF6::ConfigCore
KF6::WindowSystem
XCB::XCB
)
add_test(NAME kwin-testXcbWrapper COMMAND testXcbWrapper)
ecm_mark_as_test(testXcbWrapper)
XCB::XCB
)
add_test(NAME kwin-testXcbWrapper COMMAND testXcbWrapper)
ecm_mark_as_test(testXcbWrapper)
add_executable(testXcbSizeHints test_xcb_size_hints.cpp xcb_scaling_mock.cpp)
set_target_properties(testXcbSizeHints PROPERTIES COMPILE_DEFINITIONS "NO_NONE_WINDOW")
target_link_libraries(testXcbSizeHints
Qt::GuiPrivate
Qt::Test
Qt::Widgets
add_executable(testXcbSizeHints test_xcb_size_hints.cpp xcb_scaling_mock.cpp)
set_target_properties(testXcbSizeHints PROPERTIES COMPILE_DEFINITIONS "NO_NONE_WINDOW")
target_link_libraries(testXcbSizeHints
Qt::GuiPrivate
Qt::Test
Qt::Widgets
KF6::ConfigCore
KF6::WindowSystem
KF6::ConfigCore
KF6::WindowSystem
XCB::ICCCM
XCB::XCB
)
add_test(NAME kwin-testXcbSizeHints COMMAND testXcbSizeHints)
ecm_mark_as_test(testXcbSizeHints)
XCB::ICCCM
XCB::XCB
)
add_test(NAME kwin-testXcbSizeHints COMMAND testXcbSizeHints)
ecm_mark_as_test(testXcbSizeHints)
########################################################
# Test XcbWindow
########################################################
add_executable(testXcbWindow test_xcb_window.cpp xcb_scaling_mock.cpp)
########################################################
# Test XcbWindow
########################################################
add_executable(testXcbWindow test_xcb_window.cpp xcb_scaling_mock.cpp)
target_link_libraries(testXcbWindow
Qt::GuiPrivate
Qt::Test
Qt::Widgets
target_link_libraries(testXcbWindow
Qt::GuiPrivate
Qt::Test
Qt::Widgets
KF6::ConfigCore
KF6::WindowSystem
KF6::ConfigCore
KF6::WindowSystem
XCB::XCB
)
add_test(NAME kwin-testXcbWindow COMMAND testXcbWindow)
ecm_mark_as_test(testXcbWindow)
########################################################
# Test X11 TimestampUpdate
########################################################
add_executable(testX11TimestampUpdate test_x11_timestamp_update.cpp)
target_link_libraries(testX11TimestampUpdate
KF6::CoreAddons
Qt::Test
Qt::GuiPrivate
kwin
)
add_test(NAME kwin-testX11TimestampUpdate COMMAND testX11TimestampUpdate)
ecm_mark_as_test(testX11TimestampUpdate)
endif()
XCB::XCB
)
add_test(NAME kwin-testXcbWindow COMMAND testXcbWindow)
ecm_mark_as_test(testXcbWindow)
########################################################
# Test OnScreenNotification
@ -176,6 +161,19 @@ target_link_libraries(testGestures
add_test(NAME kwin-testGestures COMMAND testGestures)
ecm_mark_as_test(testGestures)
########################################################
# Test X11 TimestampUpdate
########################################################
add_executable(testX11TimestampUpdate test_x11_timestamp_update.cpp)
target_link_libraries(testX11TimestampUpdate
KF6::CoreAddons
Qt::Test
Qt::GuiPrivate
kwin
)
add_test(NAME kwin-testX11TimestampUpdate COMMAND testX11TimestampUpdate)
ecm_mark_as_test(testX11TimestampUpdate)
set(testOpenGLContextAttributeBuilder_SRCS
../src/opengl/abstract_opengl_context_attribute_builder.cpp
../src/opengl/egl_context_attribute_builder.cpp

View File

@ -8,7 +8,9 @@ set(mockDRM_SRCS
../../src/backends/drm/drm_commit_thread.cpp
../../src/backends/drm/drm_connector.cpp
../../src/backends/drm/drm_crtc.cpp
../../src/backends/drm/drm_dmabuf_feedback.cpp
../../src/backends/drm/drm_egl_backend.cpp
../../src/backends/drm/drm_egl_cursor_layer.cpp
../../src/backends/drm/drm_egl_layer.cpp
../../src/backends/drm/drm_egl_layer_surface.cpp
../../src/backends/drm/drm_gpu.cpp
@ -37,6 +39,7 @@ target_link_libraries(LibDrmTest
KF6::WindowSystem
KF6::CoreAddons
KF6::I18n
XCB::XCB
PkgConfig::Libxcvt
gbm::gbm
Libdrm::Libdrm

View File

@ -7,7 +7,6 @@
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <QSignalSpy>
#include <QSize>
#include <QTest>
@ -71,7 +70,6 @@ private Q_SLOTS:
void testConnectorLifetime();
void testModeset_data();
void testModeset();
void testVrrChange();
};
static void verifyCleanup(MockGpu *mockGpu)
@ -95,7 +93,7 @@ void DrmTest::testAmsDetection()
// gpu without planes should use legacy mode
{
const auto mockGpu = findPrimaryDevice(0);
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->fd, DrmDevice::open(mockGpu->devNode));
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
QVERIFY(!gpu->atomicModeSetting());
}
@ -103,8 +101,8 @@ void DrmTest::testAmsDetection()
{
const auto mockGpu = findPrimaryDevice(0);
mockGpu->planes << std::make_shared<MockPlane>(mockGpu.get(), PlaneType::Primary, 0);
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->fd, DrmDevice::open(mockGpu->devNode));
gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->fd, DrmDevice::open(mockGpu->devNode));
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
QVERIFY(gpu->atomicModeSetting());
}
@ -112,7 +110,7 @@ void DrmTest::testAmsDetection()
{
const auto mockGpu = findPrimaryDevice(0);
mockGpu->deviceCaps[MOCKDRM_DEVICE_CAP_ATOMIC] = 0;
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->fd, DrmDevice::open(mockGpu->devNode));
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
QVERIFY(!gpu->atomicModeSetting());
gpu.reset();
verifyCleanup(mockGpu.get());
@ -133,7 +131,7 @@ void DrmTest::testOutputDetection()
const auto session = Session::create(Session::Type::Noop);
const auto backend = std::make_unique<DrmBackend>(session.get());
const auto renderBackend = backend->createQPainterBackend();
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->fd, DrmDevice::open(mockGpu->devNode));
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
QVERIFY(gpu->updateOutputs());
// 3 outputs should be detected, one of them non-desktop
@ -180,7 +178,7 @@ void DrmTest::testZeroModesHandling()
const auto session = Session::create(Session::Type::Noop);
const auto backend = std::make_unique<DrmBackend>(session.get());
const auto renderBackend = backend->createQPainterBackend();
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->fd, DrmDevice::open(mockGpu->devNode));
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
// connector with zero modes should be ignored
conn->modes.clear();
@ -297,7 +295,7 @@ void DrmTest::testModeGeneration()
const auto session = Session::create(Session::Type::Noop);
const auto backend = std::make_unique<DrmBackend>(session.get());
const auto renderBackend = backend->createQPainterBackend();
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->fd, DrmDevice::open(mockGpu->devNode));
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
QFETCH(QSize, nativeMode);
QFETCH(QList<QSize>, expectedModes);
@ -340,7 +338,7 @@ void DrmTest::testConnectorLifetime()
const auto session = Session::create(Session::Type::Noop);
const auto backend = std::make_unique<DrmBackend>(session.get());
const auto renderBackend = backend->createQPainterBackend();
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->fd, DrmDevice::open(mockGpu->devNode));
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
QVERIFY(gpu->updateOutputs());
QCOMPARE(gpu->drmOutputs().size(), 1);
@ -366,8 +364,6 @@ void DrmTest::testModeset_data()
void DrmTest::testModeset()
{
// to reenable, make this part of an integration test, so that kwinApp() isn't nullptr
QSKIP("this test needs output pipelines to be enabled by default, which is no longer the case");
// test if doing a modeset would succeed
QFETCH(int, AMS);
const auto mockGpu = findPrimaryDevice(5);
@ -379,7 +375,7 @@ void DrmTest::testModeset()
const auto session = Session::create(Session::Type::Noop);
const auto backend = std::make_unique<DrmBackend>(session.get());
const auto renderBackend = backend->createQPainterBackend();
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->fd, DrmDevice::open(mockGpu->devNode));
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
QVERIFY(gpu->updateOutputs());
QCOMPARE(gpu->drmOutputs().size(), 1);
@ -388,40 +384,12 @@ void DrmTest::testModeset()
layer->beginFrame();
output->renderLoop()->prepareNewFrame();
output->renderLoop()->beginPaint();
const auto frame = std::make_shared<OutputFrame>(output->renderLoop(), std::chrono::nanoseconds(1'000'000'000'000 / output->refreshRate()));
layer->endFrame(infiniteRegion(), infiniteRegion(), frame.get());
QVERIFY(output->present(frame));
layer->endFrame(infiniteRegion(), infiniteRegion());
QVERIFY(output->present(std::make_shared<OutputFrame>(output->renderLoop())));
gpu.reset();
verifyCleanup(mockGpu.get());
}
void DrmTest::testVrrChange()
{
const auto mockGpu = findPrimaryDevice(5);
mockGpu->deviceCaps[MOCKDRM_DEVICE_CAP_ATOMIC] = 1;
const auto conn = std::make_shared<MockConnector>(mockGpu.get());
conn->setVrrCapable(false);
mockGpu->connectors.push_back(conn);
const auto session = Session::create(Session::Type::Noop);
const auto backend = std::make_unique<DrmBackend>(session.get());
const auto renderBackend = backend->createQPainterBackend();
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->fd, DrmDevice::open(mockGpu->devNode));
QVERIFY(gpu->updateOutputs());
const auto output = gpu->drmOutputs().front();
QVERIFY(!(output->capabilities() & Output::Capability::Vrr));
QSignalSpy capsChanged(output, &Output::capabilitiesChanged);
conn->setVrrCapable(true);
QVERIFY(gpu->updateOutputs());
QCOMPARE(gpu->drmOutputs().front(), output);
QCOMPARE(capsChanged.count(), 1);
QVERIFY(output->capabilities() & Output::Capability::Vrr);
}
QTEST_GUILESS_MAIN(DrmTest)
#include "drmTest.moc"

View File

@ -212,14 +212,6 @@ void MockConnector::addMode(uint32_t width, uint32_t height, float refreshRate,
free(modeInfo);
}
void MockConnector::setVrrCapable(bool cap)
{
auto &prop = *std::ranges::find_if(props, [](const auto &prop) {
return prop.name == "vrr_capable";
});
prop.value = cap ? 1 : 0;
}
//
MockCrtc::MockCrtc(MockGpu *gpu, const std::shared_ptr<MockPlane> &legacyPlane, int pipeIndex, int gamma_size)

View File

@ -71,7 +71,6 @@ public:
~MockConnector() = default;
void addMode(uint32_t width, uint32_t height, float refreshRate, bool preferred = false);
void setVrrCapable(bool cap);
drmModeConnection connection;
uint32_t type;

View File

@ -14,7 +14,7 @@ kwineffects_unit_tests(
timelinetest
)
add_executable(kwinglplatformtest kwinglplatformtest.cpp ../../src/opengl/glplatform.cpp ../../src/utils/version.cpp)
add_executable(kwinglplatformtest kwinglplatformtest.cpp mock_gl.cpp ../../src/opengl/glplatform.cpp ../../src/opengl/openglcontext.cpp ../../src/utils/version.cpp)
add_test(NAME kwineffects-kwinglplatformtest COMMAND kwinglplatformtest)
target_link_libraries(kwinglplatformtest Qt::Test Qt::Gui KF6::ConfigCore)
target_link_libraries(kwinglplatformtest Qt::Test Qt::Gui KF6::ConfigCore XCB::XCB)
ecm_mark_as_test(kwinglplatformtest)

View File

@ -6,6 +6,7 @@
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "mock_gl.h"
#include "opengl/glplatform.h"
#include <QTest>
@ -17,18 +18,33 @@ Q_DECLARE_METATYPE(KWin::ChipClass)
using namespace KWin;
void KWin::cleanupGL()
{
GLPlatform::cleanup();
}
class GLPlatformTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void cleanup();
void testDriverToString_data();
void testDriverToString();
void testChipClassToString_data();
void testChipClassToString();
void testPriorDetect();
void testDetect_data();
void testDetect();
};
void GLPlatformTest::cleanup()
{
cleanupGL();
delete s_gl;
s_gl = nullptr;
}
void GLPlatformTest::testDriverToString_data()
{
QTest::addColumn<Driver>("driver");
@ -126,6 +142,47 @@ void GLPlatformTest::testChipClassToString()
QTEST(GLPlatform::chipClassToString(chipClass), "expected");
}
void GLPlatformTest::testPriorDetect()
{
auto *gl = GLPlatform::instance();
QVERIFY(gl);
QCOMPARE(gl->supports(GLFeature::LooseBinding), false);
QCOMPARE(gl->glVersion(), Version());
QCOMPARE(gl->glslVersion(), Version());
QCOMPARE(gl->mesaVersion(), Version());
QCOMPARE(gl->driverVersion(), Version());
QCOMPARE(gl->driver(), Driver_Unknown);
QCOMPARE(gl->chipClass(), UnknownChipClass);
QCOMPARE(gl->isMesaDriver(), false);
QCOMPARE(gl->isRadeon(), false);
QCOMPARE(gl->isNvidia(), false);
QCOMPARE(gl->isIntel(), false);
QCOMPARE(gl->isPanfrost(), false);
QCOMPARE(gl->isLima(), false);
QCOMPARE(gl->isVideoCore4(), false);
QCOMPARE(gl->isVideoCore3D(), false);
QCOMPARE(gl->isVirtualBox(), false);
QCOMPARE(gl->isVMware(), false);
QCOMPARE(gl->isSoftwareEmulation(), false);
QCOMPARE(gl->isVirtualMachine(), false);
QCOMPARE(gl->glVersionString(), QByteArray());
QCOMPARE(gl->glRendererString(), QByteArray());
QCOMPARE(gl->glVendorString(), QByteArray());
QCOMPARE(gl->glShadingLanguageVersionString(), QByteArray());
QCOMPARE(gl->isLooseBinding(), false);
QCOMPARE(gl->isGLES(), false);
QCOMPARE(gl->recommendedCompositor(), QPainterCompositing);
QCOMPARE(gl->preferBufferSubData(), false);
QCOMPARE(gl->platformInterface(), NoOpenGLPlatformInterface);
}
void GLPlatformTest::testDetect_data()
{
QTest::addColumn<QString>("configFile");
@ -165,50 +222,60 @@ void GLPlatformTest::testDetect()
QFETCH(QString, configFile);
KConfig config(configFile);
const KConfigGroup driverGroup = config.group(QStringLiteral("Driver"));
s_gl = new MockGL;
s_gl->getString.vendor = driverGroup.readEntry("Vendor").toUtf8();
s_gl->getString.renderer = driverGroup.readEntry("Renderer").toUtf8();
s_gl->getString.version = driverGroup.readEntry("Version").toUtf8();
s_gl->getString.shadingLanguageVersion = driverGroup.readEntry("ShadingLanguageVersion").toUtf8();
s_gl->getString.extensions = QList<QByteArray>{QByteArrayLiteral("GL_ARB_shader_objects"),
QByteArrayLiteral("GL_ARB_fragment_shader"),
QByteArrayLiteral("GL_ARB_vertex_shader"),
QByteArrayLiteral("GL_ARB_texture_non_power_of_two")};
s_gl->getString.extensionsString = QByteArray();
const auto version = driverGroup.readEntry("Version").toUtf8();
const auto glslVersion = driverGroup.readEntry("ShadingLanguageVersion").toUtf8();
const auto renderer = driverGroup.readEntry("Renderer").toUtf8();
const auto vendor = driverGroup.readEntry("Vendor").toUtf8();
GLPlatform gl(EglPlatformInterface, version, glslVersion, renderer, vendor);
QCOMPARE(gl.platformInterface(), EglPlatformInterface);
auto *gl = GLPlatform::instance();
QVERIFY(gl);
gl->detect(EglPlatformInterface);
QCOMPARE(gl->platformInterface(), EglPlatformInterface);
const KConfigGroup settingsGroup = config.group(QStringLiteral("Settings"));
QCOMPARE(gl.isLooseBinding(), settingsGroup.readEntry("LooseBinding", false));
QCOMPARE(gl->supports(GLFeature::LooseBinding), settingsGroup.readEntry("LooseBinding", false));
QCOMPARE(gl.glVersion(), readVersion(settingsGroup, "GLVersion"));
QCOMPARE(gl.glslVersion(), readVersion(settingsGroup, "GLSLVersion"));
QCOMPARE(gl.mesaVersion(), readVersion(settingsGroup, "MesaVersion"));
QCOMPARE(gl->glVersion(), readVersion(settingsGroup, "GLVersion"));
QCOMPARE(gl->glslVersion(), readVersion(settingsGroup, "GLSLVersion"));
QCOMPARE(gl->mesaVersion(), readVersion(settingsGroup, "MesaVersion"));
QEXPECT_FAIL("amd-catalyst-radeonhd-7700M-3.1.13399", "Detects GL version instead of driver version", Continue);
QCOMPARE(gl.driverVersion(), readVersion(settingsGroup, "DriverVersion"));
QCOMPARE(gl->driverVersion(), readVersion(settingsGroup, "DriverVersion"));
QCOMPARE(gl.driver(), Driver(settingsGroup.readEntry("Driver", int(Driver_Unknown))));
QCOMPARE(gl.chipClass(), ChipClass(settingsGroup.readEntry("ChipClass", int(UnknownChipClass))));
QCOMPARE(gl->driver(), Driver(settingsGroup.readEntry("Driver", int(Driver_Unknown))));
QCOMPARE(gl->chipClass(), ChipClass(settingsGroup.readEntry("ChipClass", int(UnknownChipClass))));
QCOMPARE(gl.isMesaDriver(), settingsGroup.readEntry("Mesa", false));
QCOMPARE(gl.isRadeon(), settingsGroup.readEntry("Radeon", false));
QCOMPARE(gl.isNvidia(), settingsGroup.readEntry("Nvidia", false));
QCOMPARE(gl.isIntel(), settingsGroup.readEntry("Intel", false));
QCOMPARE(gl.isVirtualBox(), settingsGroup.readEntry("VirtualBox", false));
QCOMPARE(gl.isVMware(), settingsGroup.readEntry("VMware", false));
QCOMPARE(gl.isAdreno(), settingsGroup.readEntry("Adreno", false));
QCOMPARE(gl.isPanfrost(), settingsGroup.readEntry("Panfrost", false));
QCOMPARE(gl.isLima(), settingsGroup.readEntry("Lima", false));
QCOMPARE(gl.isVideoCore4(), settingsGroup.readEntry("VC4", false));
QCOMPARE(gl.isVideoCore3D(), settingsGroup.readEntry("V3D", false));
QCOMPARE(gl.isVirgl(), settingsGroup.readEntry("Virgl", false));
QCOMPARE(gl->isMesaDriver(), settingsGroup.readEntry("Mesa", false));
QCOMPARE(gl->isRadeon(), settingsGroup.readEntry("Radeon", false));
QCOMPARE(gl->isNvidia(), settingsGroup.readEntry("Nvidia", false));
QCOMPARE(gl->isIntel(), settingsGroup.readEntry("Intel", false));
QCOMPARE(gl->isVirtualBox(), settingsGroup.readEntry("VirtualBox", false));
QCOMPARE(gl->isVMware(), settingsGroup.readEntry("VMware", false));
QCOMPARE(gl->isAdreno(), settingsGroup.readEntry("Adreno", false));
QCOMPARE(gl->isPanfrost(), settingsGroup.readEntry("Panfrost", false));
QCOMPARE(gl->isLima(), settingsGroup.readEntry("Lima", false));
QCOMPARE(gl->isVideoCore4(), settingsGroup.readEntry("VC4", false));
QCOMPARE(gl->isVideoCore3D(), settingsGroup.readEntry("V3D", false));
QCOMPARE(gl->isVirgl(), settingsGroup.readEntry("Virgl", false));
QCOMPARE(gl.isVirtualMachine(), settingsGroup.readEntry("VirtualMachine", false));
QCOMPARE(gl->isSoftwareEmulation(), settingsGroup.readEntry("SoftwareEmulation", false));
QCOMPARE(gl->isVirtualMachine(), settingsGroup.readEntry("VirtualMachine", false));
QCOMPARE(gl.glVersionString(), version);
QCOMPARE(gl.glRendererString(), renderer);
QCOMPARE(gl.glVendorString(), vendor);
QCOMPARE(gl.glShadingLanguageVersionString(), glslVersion);
QCOMPARE(gl->glVersionString(), s_gl->getString.version);
QCOMPARE(gl->glRendererString(), s_gl->getString.renderer);
QCOMPARE(gl->glVendorString(), s_gl->getString.vendor);
QCOMPARE(gl->glShadingLanguageVersionString(), s_gl->getString.shadingLanguageVersion);
QCOMPARE(gl.isLooseBinding(), settingsGroup.readEntry("LooseBinding", false));
QCOMPARE(gl.recommendedCompositor(), CompositingType(settingsGroup.readEntry("Compositor", int(NoCompositing))));
QCOMPARE(gl.preferBufferSubData(), settingsGroup.readEntry("PreferBufferSubData", false));
QCOMPARE(gl->isLooseBinding(), settingsGroup.readEntry("LooseBinding", false));
QCOMPARE(gl->isGLES(), settingsGroup.readEntry("GLES", false));
QCOMPARE(gl->recommendedCompositor(), CompositingType(settingsGroup.readEntry("Compositor", int(NoCompositing))));
QCOMPARE(gl->preferBufferSubData(), settingsGroup.readEntry("PreferBufferSubData", false));
}
QTEST_GUILESS_MAIN(GLPlatformTest)

View File

@ -0,0 +1,57 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "mock_gl.h"
#include <epoxy/gl.h>
MockGL *s_gl = nullptr;
static const GLubyte *mock_glGetString(GLenum name)
{
if (!s_gl) {
return nullptr;
}
switch (name) {
case GL_VENDOR:
return (const GLubyte *)s_gl->getString.vendor.constData();
case GL_RENDERER:
return (const GLubyte *)s_gl->getString.renderer.constData();
case GL_VERSION:
return (const GLubyte *)s_gl->getString.version.constData();
case GL_EXTENSIONS:
return (const GLubyte *)s_gl->getString.extensionsString.constData();
case GL_SHADING_LANGUAGE_VERSION:
return (const GLubyte *)s_gl->getString.shadingLanguageVersion.constData();
default:
return nullptr;
}
}
static const GLubyte *mock_glGetStringi(GLenum name, GLuint index)
{
if (!s_gl) {
return nullptr;
}
if (name == GL_EXTENSIONS && index < uint(s_gl->getString.extensions.count())) {
return (const GLubyte *)s_gl->getString.extensions.at(index).constData();
}
return nullptr;
}
static void mock_glGetIntegerv(GLenum pname, GLint *data)
{
if (pname == GL_NUM_EXTENSIONS) {
if (data && s_gl) {
*data = s_gl->getString.extensions.count();
}
}
}
PFNGLGETSTRINGPROC epoxy_glGetString = mock_glGetString;
PFNGLGETSTRINGIPROC epoxy_glGetStringi = mock_glGetStringi;
PFNGLGETINTEGERVPROC epoxy_glGetIntegerv = mock_glGetIntegerv;

View File

@ -0,0 +1,30 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef MOCK_GL_H
#define MOCK_GL_H
#include <QByteArray>
#include <QList>
struct MockGL
{
struct
{
QByteArray vendor;
QByteArray renderer;
QByteArray version;
QList<QByteArray> extensions;
QByteArray extensionsString;
QByteArray shadingLanguageVersion;
} getString;
};
extern MockGL *s_gl;
#endif

View File

@ -25,36 +25,6 @@ qt6_generate_wayland_protocol_client_sources(KWinIntegrationTestFramework
${PLASMA_WAYLAND_PROTOCOLS_DIR}/fake-input.xml
)
if (Qt6_VERSION VERSION_LESS "6.7.1")
# the qtwaylandscanner macro cannot handle the mismatched file name and <protocol name=""
find_package(QtWaylandScanner REQUIRED)
if (WaylandProtocols_VERSION VERSION_LESS 1.36)
ecm_add_qtwayland_client_protocol(KWinIntegrationTestFramework
PROTOCOL ${WaylandProtocols_DATADIR}/staging/xdg-dialog/xdg-dialog-v1.xml
BASENAME dialog-v1
)
target_compile_definitions(KWinIntegrationTestFramework PUBLIC
-DHAVE_XDG_DIALOG_V1_HEADER=0
)
else()
ecm_add_qtwayland_client_protocol(KWinIntegrationTestFramework
PROTOCOL ${WaylandProtocols_DATADIR}/staging/xdg-dialog/xdg-dialog-v1.xml
BASENAME xdg-dialog-v1
)
target_compile_definitions(KWinIntegrationTestFramework PUBLIC
-DHAVE_XDG_DIALOG_V1_HEADER=1
)
endif()
else()
qt6_generate_wayland_protocol_client_sources(KWinIntegrationTestFramework
FILES
${WaylandProtocols_DATADIR}/staging/xdg-dialog/xdg-dialog-v1.xml
)
target_compile_definitions(KWinIntegrationTestFramework PUBLIC
-DHAVE_XDG_DIALOG_V1_HEADER=1
)
endif()
target_sources(KWinIntegrationTestFramework PRIVATE
generic_scene_opengl_test.cpp
kwin_wayland_test.cpp
@ -63,21 +33,20 @@ target_sources(KWinIntegrationTestFramework PRIVATE
target_link_libraries(KWinIntegrationTestFramework
PUBLIC
Qt::Test
Qt::Concurrent
Plasma::KWaylandClient
Wayland::Client
Libdrm::Libdrm
kwin
PRIVATE
# Own libraries
KWinXwaylandServerModule
# Static plugins
KWinQpaPlugin
KF6WindowSystemKWinPlugin
KF6IdleTimeKWinPlugin
)
if(KWIN_BUILD_X11)
target_link_libraries(KWinIntegrationTestFramework PRIVATE KWinXwaylandServerModule)
endif()
if(TARGET KF6GlobalAccelKWinPlugin)
target_link_libraries(KWinIntegrationTestFramework PUBLIC KF6GlobalAccelKWinPlugin)
endif()
@ -91,9 +60,6 @@ function(integrationTest)
set(oneValueArgs NAME)
set(multiValueArgs SRCS LIBS)
cmake_parse_arguments(ARGS "${optionArgs}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if (NOT KWIN_BUILD_X11 AND ARGS_LIBS MATCHES XCB::)
return()
endif()
add_executable(${ARGS_NAME} ${ARGS_SRCS})
target_link_libraries(${ARGS_NAME} KWinIntegrationTestFramework Qt::Test ${ARGS_LIBS})
if(${ARGS_BUILTIN_EFFECTS})
@ -101,14 +67,12 @@ function(integrationTest)
endif()
add_test(NAME kwin-${ARGS_NAME} COMMAND dbus-run-session ${CMAKE_BINARY_DIR}/bin/${ARGS_NAME})
endfunction()
if(KWIN_BUILD_X11)
integrationTest(NAME testDontCrashGlxgears SRCS dont_crash_glxgears.cpp LIBS KF6::I18n KDecoration2::KDecoration)
endif()
integrationTest(NAME testDontCrashGlxgears SRCS dont_crash_glxgears.cpp LIBS KF6::I18n KDecoration2::KDecoration)
if (KWIN_BUILD_SCREENLOCKER)
integrationTest(NAME testLockScreen SRCS lockscreen.cpp LIBS KF6::GlobalAccel)
endif()
integrationTest(NAME testBounceKeys SRCS bounce_keys_test.cpp)
integrationTest(NAME testButtonRebind SRCS buttonrebind_test.cpp)
integrationTest(NAME testDecorationInput SRCS decoration_input_test.cpp LIBS KDecoration2::KDecoration KDecoration2::KDecoration2Private)
integrationTest(NAME testInternalWindow SRCS internal_window.cpp)
integrationTest(NAME testTouchInput SRCS touch_input_test.cpp)
@ -118,12 +82,15 @@ integrationTest(NAME testPlatformCursor SRCS platformcursor.cpp)
integrationTest(NAME testDontCrashCancelAnimation SRCS dont_crash_cancel_animation.cpp LIBS KDecoration2::KDecoration)
integrationTest(NAME testTransientPlacement SRCS transient_placement.cpp)
integrationTest(NAME testDebugConsole SRCS debug_console_test.cpp)
integrationTest(NAME testDontCrashEmptyDeco SRCS dont_crash_empty_deco.cpp LIBS KDecoration2::KDecoration)
integrationTest(NAME testPlasmaSurface SRCS plasma_surface_test.cpp)
integrationTest(NAME testMaximized SRCS maximize_test.cpp LIBS KDecoration2::KDecoration KF6::Package)
integrationTest(NAME testXdgShellWindow SRCS xdgshellwindow_test.cpp LIBS KDecoration2::KDecoration)
integrationTest(NAME testXwaylandSelections SRCS xwayland_selections_test.cpp)
integrationTest(NAME testSceneOpenGL SRCS scene_opengl_test.cpp )
integrationTest(NAME testSceneOpenGLES SRCS scene_opengl_es_test.cpp )
integrationTest(NAME testScreenChanges SRCS screen_changes_test.cpp)
integrationTest(NAME testModiferOnlyShortcut SRCS modifier_only_shortcut_test.cpp LIBS XKB::XKB)
if (KWIN_BUILD_TABBOX)
integrationTest(NAME testTabBox SRCS tabbox_test.cpp)
endif()
@ -144,7 +111,7 @@ integrationTest(NAME testActivation SRCS activation_test.cpp)
integrationTest(NAME testInputMethod SRCS inputmethod_test.cpp LIBS XKB::XKB)
integrationTest(NAME testScreens SRCS screens_test.cpp)
integrationTest(NAME testScreenEdges SRCS screenedges_test.cpp LIBS XCB::ICCCM)
integrationTest(NAME testOutputChanges SRCS outputchanges_test.cpp LIBS XCB::ICCCM)
integrationTest(NAME testOutputChanges SRCS outputchanges_test.cpp)
integrationTest(NAME testTiles SRCS tiles_test.cpp)
integrationTest(NAME testFractionalScaling SRCS fractional_scaling_test.cpp)
integrationTest(NAME testMoveResize SRCS move_resize_window_test.cpp LIBS XCB::ICCCM)
@ -164,12 +131,7 @@ integrationTest(NAME testXwaylandServerRestart SRCS xwaylandserver_restart_test.
integrationTest(NAME testFakeInput SRCS fakeinput_test.cpp)
integrationTest(NAME testSecurityContext SRCS security_context_test.cpp)
integrationTest(NAME testStickyKeys SRCS sticky_keys_test.cpp)
if(KWIN_BUILD_X11)
integrationTest(NAME testDontCrashEmptyDeco SRCS dont_crash_empty_deco.cpp LIBS KDecoration2::KDecoration)
integrationTest(NAME testXwaylandSelections SRCS xwayland_selections_test.cpp)
integrationTest(NAME testXinerama SRCS xinerama_test.cpp)
integrationTest(NAME testX11KeyRead SRCS x11keyread.cpp LIBS XCB::XINPUT)
endif()
integrationTest(NAME testXinerama SRCS xinerama_test.cpp)
qt_add_dbus_interfaces(DBUS_SRCS ${CMAKE_BINARY_DIR}/src/org.kde.kwin.VirtualKeyboard.xml)
integrationTest(NAME testVirtualKeyboardDBus SRCS test_virtualkeyboard_dbus.cpp ${DBUS_SRCS})
@ -178,8 +140,8 @@ if (KWIN_BUILD_GLOBALSHORTCUTS)
integrationTest(NAME testGlobalShortcuts SRCS globalshortcuts_test.cpp LIBS XCB::ICCCM KF6::GlobalAccel KF6::I18n XKB::XKB)
integrationTest(NAME testKWinBindings SRCS kwinbindings_test.cpp LIBS KF6::I18n)
endif()
if (TARGET K::KPipeWire)
integrationTest(NAME testScreencasting SRCS screencasting_test.cpp LIBS K::KPipeWire)
if (TARGET PkgConfig::PipeWire)
integrationTest(NAME testScreencasting SRCS screencasting_test.cpp pipewirecore.cpp pipewiresourcestream.cpp LIBS Qt::GuiPrivate PkgConfig::PipeWire)
endif()
if (KWIN_BUILD_ACTIVITIES)

View File

@ -1,108 +0,0 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2024 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "kwin_wayland_test.h"
#include "pointer_input.h"
#include "wayland_server.h"
#include "workspace.h"
#include <KWayland/Client/keyboard.h>
#include <KWayland/Client/seat.h>
#include <linux/input-event-codes.h>
using namespace KWin;
static const QString s_socketName = QStringLiteral("wayland_test_kwin_buttonrebind-0");
class TestButtonRebind : public QObject
{
Q_OBJECT
private Q_SLOTS:
void init();
void cleanup();
void initTestCase();
void testKey_data();
void testKey();
private:
quint32 timestamp = 1;
};
void TestButtonRebind::init()
{
QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Seat));
QVERIFY(Test::waitForWaylandKeyboard());
}
void TestButtonRebind::cleanup()
{
Test::destroyWaylandConnection();
QVERIFY(QFile::remove(QStandardPaths::locate(QStandardPaths::ConfigLocation, QStringLiteral("kcminputrc"))));
}
void TestButtonRebind::initTestCase()
{
qRegisterMetaType<KWin::Window *>();
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
QVERIFY(waylandServer()->init(s_socketName));
Test::setOutputConfig({
QRect(0, 0, 1280, 1024),
QRect(1280, 0, 1280, 1024),
});
kwinApp()->start();
QVERIFY(applicationStartedSpy.wait());
}
void TestButtonRebind::testKey_data()
{
QTest::addColumn<QKeySequence>("boundKeys");
QTest::addColumn<QList<quint32>>("expectedKeys");
QTest::newRow("single key") << QKeySequence(Qt::Key_A) << QList<quint32>{KEY_A};
QTest::newRow("single modifier") << QKeySequence(Qt::Key_Control) << QList<quint32>{KEY_LEFTCTRL};
QTest::newRow("single modifier plus key") << QKeySequence(Qt::ControlModifier | Qt::Key_N) << QList<quint32>{KEY_LEFTCTRL, KEY_N};
QTest::newRow("multiple modifiers plus key") << QKeySequence(Qt::ControlModifier | Qt::MetaModifier | Qt::Key_Y) << QList<quint32>{KEY_LEFTCTRL, KEY_LEFTMETA, KEY_Y};
QTest::newRow("delete") << QKeySequence(Qt::Key_Delete) << QList<quint32>{KEY_DELETE};
QTest::newRow("keypad delete") << QKeySequence(Qt::KeypadModifier | Qt::Key_Delete) << QList<quint32>{KEY_KPDOT};
QTest::newRow("keypad enter") << QKeySequence(Qt::KeypadModifier | Qt::Key_Enter) << QList<quint32>{KEY_KPENTER};
}
void TestButtonRebind::testKey()
{
KConfigGroup buttonGroup = KSharedConfig::openConfig(QStringLiteral("kcminputrc"))->group(QStringLiteral("ButtonRebinds")).group(QStringLiteral("Mouse"));
QFETCH(QKeySequence, boundKeys);
buttonGroup.writeEntry("ExtraButton7", QStringList{"Key", boundKeys.toString(QKeySequence::PortableText)}, KConfig::Notify);
buttonGroup.sync();
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
std::unique_ptr<KWayland::Client::Keyboard> keyboard(Test::waylandSeat()->createKeyboard());
QSignalSpy enteredSpy(keyboard.get(), &KWayland::Client::Keyboard::entered);
QSignalSpy keyChangedSpy(keyboard.get(), &KWayland::Client::Keyboard::keyChanged);
QVERIFY(enteredSpy.wait());
// 0x119 is Qt::ExtraButton7
Test::pointerButtonPressed(0x119, timestamp++);
QVERIFY(keyChangedSpy.wait());
QFETCH(QList<quint32>, expectedKeys);
QCOMPARE(keyChangedSpy.count(), expectedKeys.count());
for (int i = 0; i < keyChangedSpy.count(); i++) {
QCOMPARE(keyChangedSpy.at(i).at(0).value<quint32>(), expectedKeys.at(i));
QCOMPARE(keyChangedSpy.at(i).at(1).value<KWayland::Client::Keyboard::KeyState>(), KWayland::Client::Keyboard::KeyState::Pressed);
}
Test::pointerButtonReleased(0x119, timestamp++);
}
WAYLANDTEST_MAIN(TestButtonRebind)
#include "buttonrebind_test.moc"

View File

@ -11,14 +11,11 @@
#include "core/output.h"
#include "debug_console.h"
#include "internalwindow.h"
#include "utils/xcbutils.h"
#include "wayland_server.h"
#include "window.h"
#include "workspace.h"
#if KWIN_BUILD_X11
#include "utils/xcbutils.h"
#endif
#include <KWayland/Client/compositor.h>
#include <KWayland/Client/connection_thread.h>
#include <KWayland/Client/shm_pool.h>
@ -41,10 +38,8 @@ private Q_SLOTS:
void cleanup();
void topLevelTest_data();
void topLevelTest();
#if KWIN_BUILD_X11
void testX11Window();
void testX11Unmanaged();
#endif
void testWaylandClient();
void testInternalWindow();
void testClosingDebugConsole();
@ -114,7 +109,6 @@ void DebugConsoleTest::topLevelTest()
}
}
#if KWIN_BUILD_X11
void DebugConsoleTest::testX11Window()
{
DebugConsoleModel model;
@ -287,7 +281,6 @@ void DebugConsoleTest::testX11Unmanaged()
QVERIFY(!model.hasChildren(unmanagedTopLevelIndex));
QVERIFY(!model2.hasChildren(model2.index(1, 0, QModelIndex())));
}
#endif
void DebugConsoleTest::testWaylandClient()
{

View File

@ -52,8 +52,10 @@ private Q_SLOTS:
void cleanup();
void testAxis_data();
void testAxis();
void testDoubleClickOnAllDesktops_data();
void testDoubleClickOnAllDesktops();
void testDoubleClickClose();
void testDoubleTap_data();
void testDoubleTap();
void testHover();
void testPressToMove_data();
@ -70,7 +72,7 @@ private Q_SLOTS:
void testTooltipDoesntEatKeyEvents();
private:
std::tuple<Window *, std::unique_ptr<KWayland::Client::Surface>, std::unique_ptr<Test::XdgToplevel>, std::unique_ptr<Test::XdgToplevelDecorationV1>> showWindow();
std::tuple<Window *, std::unique_ptr<KWayland::Client::Surface>, Test::XdgToplevel *> showWindow();
};
#define MOTION(target) Test::pointerMotion(target, timestamp++)
@ -79,23 +81,23 @@ private:
#define RELEASE Test::pointerButtonReleased(BTN_LEFT, timestamp++)
std::tuple<Window *, std::unique_ptr<KWayland::Client::Surface>, std::unique_ptr<Test::XdgToplevel>, std::unique_ptr<Test::XdgToplevelDecorationV1>> DecorationInputTest::showWindow()
std::tuple<Window *, std::unique_ptr<KWayland::Client::Surface>, Test::XdgToplevel *> DecorationInputTest::showWindow()
{
#define VERIFY(statement) \
if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__)) \
return {nullptr, nullptr, nullptr, nullptr};
return {nullptr, nullptr, nullptr};
#define COMPARE(actual, expected) \
if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__)) \
return {nullptr, nullptr, nullptr, nullptr};
return {nullptr, nullptr, nullptr};
std::unique_ptr<KWayland::Client::Surface> surface{Test::createSurface()};
VERIFY(surface.get());
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly);
VERIFY(shellSurface.get());
std::unique_ptr<Test::XdgToplevelDecorationV1> decoration = Test::createXdgToplevelDecorationV1(shellSurface.get());
VERIFY(decoration.get());
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly, surface.get());
VERIFY(shellSurface);
Test::XdgToplevelDecorationV1 *decoration = Test::createXdgToplevelDecorationV1(shellSurface, shellSurface);
VERIFY(decoration);
QSignalSpy decorationConfigureRequestedSpy(decoration.get(), &Test::XdgToplevelDecorationV1::configureRequested);
QSignalSpy decorationConfigureRequestedSpy(decoration, &Test::XdgToplevelDecorationV1::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
decoration->set_mode(Test::XdgToplevelDecorationV1::mode_server_side);
@ -112,7 +114,7 @@ std::tuple<Window *, std::unique_ptr<KWayland::Client::Surface>, std::unique_ptr
#undef VERIFY
#undef COMPARE
return {window, std::move(surface), std::move(shellSurface), std::move(decoration)};
return {window, std::move(surface), shellSurface};
}
void DecorationInputTest::initTestCase()
@ -172,7 +174,7 @@ void DecorationInputTest::testAxis()
{
static constexpr double oneTick = 15;
const auto [window, surface, shellSurface, decoration] = showWindow();
const auto [window, surface, shellSurface] = showWindow();
QVERIFY(window);
QVERIFY(window->isDecorated());
QVERIFY(!window->noBorder());
@ -209,6 +211,16 @@ void DecorationInputTest::testAxis()
QVERIFY(!window->keepAbove());
}
void DecorationInputTest::testDoubleClickOnAllDesktops_data()
{
QTest::addColumn<QPoint>("decoPoint");
QTest::addColumn<Qt::WindowFrameSection>("expectedSection");
QTest::newRow("topLeft") << QPoint(0, 0) << Qt::TopLeftSection;
QTest::newRow("top") << QPoint(250, 0) << Qt::TopSection;
QTest::newRow("topRight") << QPoint(499, 0) << Qt::TopRightSection;
}
void KWin::DecorationInputTest::testDoubleClickOnAllDesktops()
{
KConfigGroup group = kwinApp()->config()->group(QStringLiteral("Windows"));
@ -216,7 +228,7 @@ void KWin::DecorationInputTest::testDoubleClickOnAllDesktops()
group.sync();
workspace()->slotReconfigure();
const auto [window, surface, shellSurface, decoration] = showWindow();
const auto [window, surface, shellSurface] = showWindow();
QVERIFY(window);
QVERIFY(window->isDecorated());
QVERIFY(!window->noBorder());
@ -237,6 +249,21 @@ void KWin::DecorationInputTest::testDoubleClickOnAllDesktops()
PRESS;
RELEASE;
QVERIFY(!window->isOnAllDesktops());
// test top most deco pixel, BUG: 362860
window->move(QPoint(0, 0));
QFETCH(QPoint, decoPoint);
MOTION(decoPoint);
QVERIFY(input()->pointer()->decoration());
QCOMPARE(input()->pointer()->decoration()->window(), window);
QTEST(input()->pointer()->decoration()->decoration()->sectionUnderMouse(), "expectedSection");
// double click
PRESS;
RELEASE;
QVERIFY(!window->isOnAllDesktops());
PRESS;
RELEASE;
QVERIFY(window->isOnAllDesktops());
}
void DecorationInputTest::testDoubleClickClose()
@ -247,13 +274,13 @@ void DecorationInputTest::testDoubleClickClose()
group.sync();
workspace()->slotReconfigure();
auto [window, surface, shellSurface, decoration] = showWindow();
auto [window, surface, shellSurface] = showWindow();
QVERIFY(window);
QVERIFY(window->isDecorated());
quint32 timestamp = 1;
MOTION(QPoint(window->frameGeometry().center().x(), window->frameMargins().top() / 2.0));
connect(shellSurface.get(), &Test::XdgToplevel::closeRequested, this, [&surface = surface]() {
connect(shellSurface, &Test::XdgToplevel::closeRequested, this, [&surface = surface]() {
surface.reset();
});
@ -270,6 +297,16 @@ void DecorationInputTest::testDoubleClickClose()
window->unref();
}
void DecorationInputTest::testDoubleTap_data()
{
QTest::addColumn<QPoint>("decoPoint");
QTest::addColumn<Qt::WindowFrameSection>("expectedSection");
QTest::newRow("topLeft") << QPoint(10, 10) << Qt::TopLeftSection;
QTest::newRow("top") << QPoint(260, 10) << Qt::TopSection;
QTest::newRow("topRight") << QPoint(509, 10) << Qt::TopRightSection;
}
void KWin::DecorationInputTest::testDoubleTap()
{
KConfigGroup group = kwinApp()->config()->group(QStringLiteral("Windows"));
@ -277,7 +314,7 @@ void KWin::DecorationInputTest::testDoubleTap()
group.sync();
workspace()->slotReconfigure();
const auto [window, surface, shellSurface, decoration] = showWindow();
const auto [window, surface, shellSurface] = showWindow();
QVERIFY(window);
QVERIFY(window->isDecorated());
QVERIFY(!window->noBorder());
@ -298,11 +335,28 @@ void KWin::DecorationInputTest::testDoubleTap()
Test::touchDown(0, tapPoint, timestamp++);
Test::touchUp(0, timestamp++);
QVERIFY(!window->isOnAllDesktops());
// test top most deco pixel, BUG: 362860
//
// Not directly at (0, 0), otherwise ScreenEdgeInputFilter catches
// event before DecorationEventFilter.
window->move(QPoint(10, 10));
QFETCH(QPoint, decoPoint);
// double click
Test::touchDown(0, decoPoint, timestamp++);
QVERIFY(input()->touch()->decoration());
QCOMPARE(input()->touch()->decoration()->window(), window);
QTEST(input()->touch()->decoration()->decoration()->sectionUnderMouse(), "expectedSection");
Test::touchUp(0, timestamp++);
QVERIFY(!window->isOnAllDesktops());
Test::touchDown(0, decoPoint, timestamp++);
Test::touchUp(0, timestamp++);
QVERIFY(window->isOnAllDesktops());
}
void DecorationInputTest::testHover()
{
const auto [window, surface, shellSurface, decoration] = showWindow();
const auto [window, surface, shellSurface] = showWindow();
QVERIFY(window);
QVERIFY(window->isDecorated());
QVERIFY(!window->noBorder());
@ -361,7 +415,7 @@ void DecorationInputTest::testPressToMove_data()
void DecorationInputTest::testPressToMove()
{
const auto [window, surface, shellSurface, decoration] = showWindow();
const auto [window, surface, shellSurface] = showWindow();
QVERIFY(window);
QVERIFY(window->isDecorated());
QVERIFY(!window->noBorder());
@ -418,7 +472,7 @@ void DecorationInputTest::testTapToMove_data()
void DecorationInputTest::testTapToMove()
{
const auto [window, surface, shellSurface, decoration] = showWindow();
const auto [window, surface, shellSurface] = showWindow();
QVERIFY(window);
QVERIFY(window->isDecorated());
QVERIFY(!window->noBorder());
@ -482,7 +536,7 @@ void DecorationInputTest::testResizeOutsideWindow()
workspace()->slotReconfigure();
// now create window
const auto [window, surface, shellSurface, decoration] = showWindow();
const auto [window, surface, shellSurface] = showWindow();
QVERIFY(window);
QVERIFY(window->isDecorated());
QVERIFY(!window->noBorder());
@ -576,7 +630,7 @@ void DecorationInputTest::testModifierClickUnrestrictedMove()
QCOMPARE(options->commandAll3(), Options::MouseUnrestrictedMove);
// create a window
const auto [window, surface, shellSurface, decoration] = showWindow();
const auto [window, surface, shellSurface] = showWindow();
QVERIFY(window);
QVERIFY(window->isDecorated());
QVERIFY(!window->noBorder());
@ -638,7 +692,7 @@ void DecorationInputTest::testModifierScrollOpacity()
group.sync();
workspace()->slotReconfigure();
const auto [window, surface, shellSurface, decoration] = showWindow();
const auto [window, surface, shellSurface] = showWindow();
QVERIFY(window);
QVERIFY(window->isDecorated());
QVERIFY(!window->noBorder());
@ -696,7 +750,7 @@ void DecorationInputTest::testTouchEvents()
{
// this test verifies that the decoration gets a hover leave event on touch release
// see BUG 386231
const auto [window, surface, shellSurface, decoration] = showWindow();
const auto [window, surface, shellSurface] = showWindow();
QVERIFY(window);
QVERIFY(window->isDecorated());
QVERIFY(!window->noBorder());
@ -742,7 +796,7 @@ void DecorationInputTest::testTooltipDoesntEatKeyEvents()
QVERIFY(keyboard);
QSignalSpy enteredSpy(keyboard, &KWayland::Client::Keyboard::entered);
const auto [window, surface, shellSurface, decoration] = showWindow();
const auto [window, surface, shellSurface] = showWindow();
QVERIFY(window);
QVERIFY(window->isDecorated());
QVERIFY(!window->noBorder());

View File

@ -131,7 +131,7 @@ void X11DesktopWindowTest::testDesktopWindow()
QVERIFY(window);
QCOMPARE(window->window(), windowId);
QVERIFY(!window->isDecorated());
QCOMPARE(window->windowType(), WindowType::Desktop);
QCOMPARE(window->windowType(), NET::Desktop);
QCOMPARE(window->frameGeometry(), windowGeometry);
QVERIFY(window->isDesktop());
QCOMPARE(window->depth(), 24);

View File

@ -15,9 +15,7 @@
#include "wayland_server.h"
#include "window.h"
#include "workspace.h"
#if KWIN_BUILD_X11
#include "x11window.h"
#endif
#include <KDecoration2/Decoration>
@ -87,7 +85,7 @@ void DontCrashCancelAnimationFromAnimationEndedTest::testScript()
// create a window
std::unique_ptr<KWayland::Client::Surface> surface{Test::createSurface()};
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
// let's render
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);

View File

@ -182,7 +182,7 @@ void ScriptedEffectsTest::testEffectsHandler()
// create a window
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
auto *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
shellSurface->set_title("WindowA");
auto *c = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
@ -265,7 +265,7 @@ void ScriptedEffectsTest::testAnimations()
// animated after window added connect
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
auto *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
shellSurface->set_title("Window 1");
auto *c = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
@ -371,7 +371,7 @@ void ScriptedEffectsTest::testFullScreenEffect()
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
auto *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
shellSurface->set_title("Window 1");
auto *c = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
@ -433,7 +433,7 @@ void ScriptedEffectsTest::testKeepAlive()
// create a window
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
auto *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
auto *c = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
QVERIFY(c);
@ -479,7 +479,7 @@ void ScriptedEffectsTest::testGrab()
// create test window
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
QVERIFY(window);
@ -509,7 +509,7 @@ void ScriptedEffectsTest::testGrabAlreadyGrabbedWindow()
// create test window
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
QVERIFY(window);
@ -543,7 +543,7 @@ void ScriptedEffectsTest::testGrabAlreadyGrabbedWindowForced()
// create test window
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
QVERIFY(window);
@ -572,7 +572,7 @@ void ScriptedEffectsTest::testUngrab()
// create test window
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
QVERIFY(window);
@ -614,7 +614,7 @@ void ScriptedEffectsTest::testRedirect()
// create test window
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
QVERIFY(window);
@ -690,7 +690,7 @@ void ScriptedEffectsTest::testComplete()
// create test window
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
QVERIFY(window);

View File

@ -42,8 +42,7 @@ private Q_SLOTS:
void init();
void cleanup();
void testToplevel();
void testPopup();
void testShow();
};
void TestFractionalScale::initTestCase()
@ -88,73 +87,20 @@ void TestFractionalScale::cleanup()
Test::destroyWaylandConnection();
}
void TestFractionalScale::testToplevel()
void TestFractionalScale::testShow()
{
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
std::unique_ptr<Test::FractionalScaleV1> fractionalScale(Test::createFractionalScaleV1(surface.get()));
QSignalSpy fractionalScaleChanged(fractionalScale.get(), &Test::FractionalScaleV1::preferredScaleChanged);
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
// above call commits the surface and blocks for the configure event. We should have received the scale already
// We are sent the value in 120ths
QCOMPARE(fractionalScaleChanged.count(), 1);
QCOMPARE(fractionalScale->preferredScale(), std::round(1.25 * 120));
QCOMPARE(fractionalScale->preferredScale(), 1.25 * 120);
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
QCOMPARE(fractionalScaleChanged.count(), 1);
QCOMPARE(fractionalScale->preferredScale(), std::round(1.25 * 120));
QVERIFY(window);
// move to screen 2
window->move(QPoint(1280, 0));
QVERIFY(Test::waylandSync());
QCOMPARE(fractionalScaleChanged.count(), 2);
QCOMPARE(fractionalScale->preferredScale(), std::round(2.0 * 120));
}
void TestFractionalScale::testPopup()
{
std::unique_ptr<KWayland::Client::Surface> toplevelSurface(Test::createSurface());
std::unique_ptr<Test::FractionalScaleV1> toplevelFractionalScale(Test::createFractionalScaleV1(toplevelSurface.get()));
QSignalSpy toplevelFractionalScaleChanged(toplevelFractionalScale.get(), &Test::FractionalScaleV1::preferredScaleChanged);
std::unique_ptr<Test::XdgToplevel> toplevel(Test::createXdgToplevelSurface(toplevelSurface.get()));
// above call commits the surface and blocks for the configure event. We should have received the scale already
// We are sent the value in 120ths
QCOMPARE(toplevelFractionalScaleChanged.count(), 1);
QCOMPARE(toplevelFractionalScale->preferredScale(), std::round(1.25 * 120));
auto toplevelWindow = Test::renderAndWaitForShown(toplevelSurface.get(), QSize(100, 50), Qt::blue);
QVERIFY(toplevelWindow);
QCOMPARE(toplevelFractionalScaleChanged.count(), 1);
QCOMPARE(toplevelFractionalScale->preferredScale(), std::round(1.25 * 120));
std::unique_ptr<KWayland::Client::Surface> popupSurface(Test::createSurface());
std::unique_ptr<Test::FractionalScaleV1> popupFractionalScale(Test::createFractionalScaleV1(popupSurface.get()));
QSignalSpy popupFractionalScaleChanged(popupFractionalScale.get(), &Test::FractionalScaleV1::preferredScaleChanged);
std::unique_ptr<Test::XdgPositioner> positioner(Test::createXdgPositioner());
positioner->set_size(10, 10);
positioner->set_anchor_rect(10, 10, 10, 10);
std::unique_ptr<Test::XdgPopup> popup(Test::createXdgPopupSurface(popupSurface.get(), toplevel->xdgSurface(), positioner.get()));
// above call commits the surface and blocks for the configure event. We should have received the scale already
// We are sent the value in 120ths
QCOMPARE(popupFractionalScaleChanged.count(), 1);
QCOMPARE(popupFractionalScale->preferredScale(), std::round(1.25 * 120));
auto popupWindow = Test::renderAndWaitForShown(popupSurface.get(), QSize(10, 10), Qt::cyan);
QVERIFY(popupWindow);
QCOMPARE(popupFractionalScaleChanged.count(), 1);
QCOMPARE(popupFractionalScale->preferredScale(), std::round(1.25 * 120));
// move the parent to screen 2
toplevelWindow->move(QPoint(1280, 0));
QVERIFY(Test::waylandSync());
QCOMPARE(toplevelFractionalScaleChanged.count(), 2);
QCOMPARE(toplevelFractionalScale->preferredScale(), std::round(2.0 * 120));
QCOMPARE(popupFractionalScaleChanged.count(), 2);
QCOMPARE(popupFractionalScale->preferredScale(), std::round(2.0 * 120));
QCOMPARE(fractionalScale->preferredScale(), 1.25 * 120);
}
WAYLANDTEST_MAIN(TestFractionalScale)

View File

@ -101,7 +101,7 @@ void InputStackingOrderTest::testPointerFocusUpdatesOnStackingOrderChange()
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
std::unique_ptr<KWayland::Client::Surface> surface1 = Test::createSurface();
QVERIFY(surface1);
std::unique_ptr<Test::XdgToplevel> shellSurface1 = Test::createXdgToplevelSurface(surface1.get());
Test::XdgToplevel *shellSurface1 = Test::createXdgToplevelSurface(surface1.get(), surface1.get());
QVERIFY(shellSurface1);
render(surface1.get());
QVERIFY(windowAddedSpy.wait());
@ -110,7 +110,7 @@ void InputStackingOrderTest::testPointerFocusUpdatesOnStackingOrderChange()
std::unique_ptr<KWayland::Client::Surface> surface2 = Test::createSurface();
QVERIFY(surface2);
std::unique_ptr<Test::XdgToplevel> shellSurface2 = Test::createXdgToplevelSurface(surface2.get());
Test::XdgToplevel *shellSurface2 = Test::createXdgToplevelSurface(surface2.get(), surface2.get());
QVERIFY(shellSurface2);
render(surface2.get());
QVERIFY(windowAddedSpy.wait());

View File

@ -213,12 +213,12 @@ void InputMethodTest::testEnableDisableV3()
kwinApp()->inputMethod()->hide();
QVERIFY(!keyboardClient->isShown());
QSignalSpy hiddenChangedSpy(keyboardClient, &Window::hiddenChanged);
QSignalSpy windowShownSpy(keyboardClient, &Window::windowShown);
// Force enable the text input object. This is what's done by Gtk.
textInputV3->enable();
textInputV3->commit();
hiddenChangedSpy.wait();
windowShownSpy.wait();
QVERIFY(keyboardClient->isShown());
// disable text input and ensure that it is not hiding input panel without commit
@ -361,7 +361,7 @@ void InputMethodTest::testSwitchFocusedSurfaces()
QList<Window *> windows;
std::vector<std::unique_ptr<KWayland::Client::Surface>> surfaces;
std::vector<std::unique_ptr<Test::XdgToplevel>> toplevels;
QList<Test::XdgToplevel *> toplevels;
// We create 3 surfaces
for (int i = 0; i < 3; ++i) {
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
@ -369,7 +369,7 @@ void InputMethodTest::testSwitchFocusedSurfaces()
windows += Test::renderAndWaitForShown(surface.get(), QSize(1280, 1024), Qt::red);
QCOMPARE(workspace()->activeWindow(), windows.constLast());
surfaces.push_back(std::move(surface));
toplevels.push_back(std::move(shellSurface));
toplevels += shellSurface;
}
QCOMPARE(windowAddedSpy.count(), 3);
waylandServer()->seat()->setFocusedTextInputSurface(windows.constFirst()->surface());
@ -388,6 +388,12 @@ void InputMethodTest::testSwitchFocusedSurfaces()
waylandServer()->seat()->setFocusedTextInputSurface(windows.first()->surface());
QVERIFY(activateSpy.count() || activateSpy.wait());
QVERIFY(!kwinApp()->inputMethod()->isActive());
// Destroy the test window.
for (int i = 0; i < windows.count(); ++i) {
delete toplevels[i];
QVERIFY(Test::waitForWindowClosed(windows[i]));
}
}
void InputMethodTest::testV2V3SameClient()

View File

@ -500,6 +500,16 @@ void InternalWindowTest::testMove()
internalWindow->move(QPoint(10, 20));
QCOMPARE(internalWindow->frameGeometry(), QRect(10, 20, 100, 100));
QTRY_COMPARE(win.geometry(), QRect(10, 20, 100, 100));
// now move with a Geometry update blocker
{
GeometryUpdatesBlocker blocker(internalWindow);
internalWindow->move(QPoint(5, 10));
// not synced!
QCOMPARE(win.geometry(), QRect(10, 20, 100, 100));
}
// after destroying the blocker it should be synced
QTRY_COMPARE(win.geometry(), QRect(5, 10, 100, 100));
}
void InternalWindowTest::testSkipCloseAnimation_data()

View File

@ -15,14 +15,11 @@
#include "inputmethod.h"
#include "placement.h"
#include "pluginmanager.h"
#include "utils/xcbutils.h"
#include "wayland_server.h"
#include "workspace.h"
#if KWIN_BUILD_X11
#include "utils/xcbutils.h"
#include "xwayland/xwayland.h"
#include "xwayland/xwaylandlauncher.h"
#endif
#include <KPluginMetaData>
@ -55,7 +52,6 @@ WaylandTestApplication::WaylandTestApplication(OperationMode mode, int &argc, ch
const QStringList configs{
QStringLiteral("kaccessrc"),
QStringLiteral("kglobalshortcutsrc"),
QStringLiteral("kcminputrc"),
};
for (const QString &config : configs) {
if (const QString &fileName = QStandardPaths::locate(QStandardPaths::ConfigLocation, config); !fileName.isEmpty()) {
@ -83,10 +79,6 @@ WaylandTestApplication::WaylandTestApplication(OperationMode mode, int &argc, ch
KConfigGroup windowsGroup = config->group(QStringLiteral("Windows"));
windowsGroup.writeEntry("Placement", Placement::policyToString(PlacementSmart));
windowsGroup.sync();
KConfigGroup edgeBarrierGroup = config->group(QStringLiteral("EdgeBarrier"));
edgeBarrierGroup.writeEntry("EdgeBarrier", 0);
edgeBarrierGroup.writeEntry("CornerBarrier", false);
edgeBarrierGroup.sync();
setConfig(config);
const auto ownPath = libraryPaths().last();
@ -95,7 +87,7 @@ WaylandTestApplication::WaylandTestApplication(OperationMode mode, int &argc, ch
setSession(Session::create(Session::Type::Noop));
setOutputBackend(std::make_unique<VirtualBackend>());
m_waylandServer.reset(WaylandServer::create());
WaylandServer::create(this);
setProcessStartupEnvironment(QProcessEnvironment::systemEnvironment());
}
@ -107,16 +99,13 @@ WaylandTestApplication::~WaylandTestApplication()
if (effects) {
effects->unloadAllEffects();
}
#if KWIN_BUILD_X11
m_xwayland.reset();
#endif
destroyVirtualInputDevices();
destroyColorManager();
destroyWorkspace();
destroyInputMethod();
destroyCompositor();
destroyInput();
m_waylandServer.reset();
}
void WaylandTestApplication::createVirtualInputDevices()
@ -190,12 +179,10 @@ void WaylandTestApplication::continueStartupWithScene()
qFatal("Failed to initialize the Wayland server, exiting now");
}
#if KWIN_BUILD_X11
if (operationMode() == OperationModeXwayland) {
m_xwayland = std::make_unique<Xwl::Xwayland>(this);
m_xwayland->init();
}
#endif
notifyStarted();
}
@ -215,12 +202,10 @@ Test::VirtualInputDevice *WaylandTestApplication::virtualTouch() const
return m_virtualTouch.get();
}
#if KWIN_BUILD_X11
XwaylandInterface *WaylandTestApplication::xwayland() const
{
return m_xwayland.get();
}
#endif
Test::FractionalScaleManagerV1::~FractionalScaleManagerV1()
{
@ -239,11 +224,7 @@ int Test::FractionalScaleV1::preferredScale()
void Test::FractionalScaleV1::wp_fractional_scale_v1_preferred_scale(uint32_t scale)
{
if (m_preferredScale == scale) {
return;
}
m_preferredScale = scale;
Q_EMIT preferredScaleChanged();
}
void Test::setOutputConfig(const QList<QRect> &geometries)

View File

@ -34,11 +34,6 @@
#include "qwayland-xdg-decoration-unstable-v1.h"
#include "qwayland-xdg-shell.h"
#include "qwayland-zkde-screencast-unstable-v1.h"
#if HAVE_XDG_DIALOG_V1_HEADER
#include "qwayland-xdg-dialog-v1.h"
#else
#include "qwayland-dialog-v1.h"
#endif
namespace KWayland
{
@ -73,15 +68,10 @@ class ScreencastingV1;
namespace KWin
{
class WaylandServer;
#if KWIN_BUILD_X11
namespace Xwl
{
class Xwayland;
}
#endif
namespace Test
{
@ -103,9 +93,7 @@ public:
Test::VirtualInputDevice *virtualPointer() const;
Test::VirtualInputDevice *virtualKeyboard() const;
Test::VirtualInputDevice *virtualTouch() const;
#if KWIN_BUILD_X11
XwaylandInterface *xwayland() const override;
#endif
protected:
void performStartup() override;
@ -117,10 +105,7 @@ private:
void createVirtualInputDevices();
void destroyVirtualInputDevices();
std::unique_ptr<WaylandServer> m_waylandServer;
#if KWIN_BUILD_X11
std::unique_ptr<Xwl::Xwayland> m_xwayland;
#endif
QString m_inputMethodServerToStart;
std::unique_ptr<Test::VirtualInputDevice> m_virtualPointer;
@ -489,7 +474,7 @@ protected:
private:
std::unique_ptr<KWayland::Client::Surface> m_inputSurface;
std::unique_ptr<QtWayland::zwp_input_panel_surface_v1> m_inputMethodSurface;
QtWayland::zwp_input_panel_surface_v1 *m_inputMethodSurface = nullptr;
struct ::zwp_input_method_context_v1 *m_context = nullptr;
Mode m_mode = Mode::TopLevel;
};
@ -511,11 +496,8 @@ public:
protected:
void wp_fractional_scale_v1_preferred_scale(uint32_t scale) override;
Q_SIGNALS:
void preferredScaleChanged();
private:
uint m_preferredScale = 120;
int m_preferredScale = 120;
};
class ScreenEdgeManagerV1 : public QObject, public QtWayland::kde_screen_edge_manager_v1
@ -560,19 +542,6 @@ public:
~SecurityContextManagerV1() override;
};
class XdgWmDialogV1 : public QtWayland::xdg_wm_dialog_v1
{
public:
~XdgWmDialogV1() override;
};
class XdgDialogV1 : public QtWayland::xdg_dialog_v1
{
public:
XdgDialogV1(XdgWmDialogV1 *wm, XdgToplevel *toplevel);
~XdgDialogV1() override;
};
enum class AdditionalWaylandInterface {
Seat = 1 << 0,
PlasmaShell = 1 << 2,
@ -594,7 +563,6 @@ enum class AdditionalWaylandInterface {
CursorShapeV1 = 1 << 18,
FakeInput = 1 << 19,
SecurityContextManagerV1 = 1 << 20,
XdgDialogV1 = 1 << 21,
};
Q_DECLARE_FLAGS(AdditionalWaylandInterfaces, AdditionalWaylandInterface)
@ -621,6 +589,7 @@ public:
void setLeds(LEDs leds) override;
bool isKeyboard() const override;
bool isAlphaNumericKeyboard() const override;
bool isPointer() const override;
bool isTouchpad() const override;
bool isTouch() const override;
@ -706,13 +675,13 @@ void flushWaylandConnection();
bool waylandSync();
std::unique_ptr<KWayland::Client::Surface> createSurface();
std::unique_ptr<KWayland::Client::SubSurface> createSubSurface(KWayland::Client::Surface *surface,
KWayland::Client::Surface *parentSurface);
KWayland::Client::SubSurface *createSubSurface(KWayland::Client::Surface *surface,
KWayland::Client::Surface *parentSurface, QObject *parent = nullptr);
std::unique_ptr<LayerSurfaceV1> createLayerSurfaceV1(KWayland::Client::Surface *surface,
const QString &scope,
KWayland::Client::Output *output = nullptr,
LayerShellV1::layer layer = LayerShellV1::layer_top);
LayerSurfaceV1 *createLayerSurfaceV1(KWayland::Client::Surface *surface,
const QString &scope,
KWayland::Client::Output *output = nullptr,
LayerShellV1::layer layer = LayerShellV1::layer_top);
TextInputManagerV3 *waylandTextInputManagerV3();
@ -721,27 +690,28 @@ enum class CreationSetup {
CreateAndConfigure, /// commit and wait for the configure event, making this surface ready to commit buffers
};
std::unique_ptr<QtWayland::zwp_input_panel_surface_v1> createInputPanelSurfaceV1(KWayland::Client::Surface *surface,
KWayland::Client::Output *output,
MockInputMethod::Mode mode);
QtWayland::zwp_input_panel_surface_v1 *createInputPanelSurfaceV1(KWayland::Client::Surface *surface,
KWayland::Client::Output *output,
MockInputMethod::Mode mode);
std::unique_ptr<FractionalScaleV1> createFractionalScaleV1(KWayland::Client::Surface *surface);
FractionalScaleV1 *createFractionalScaleV1(KWayland::Client::Surface *surface);
std::unique_ptr<XdgToplevel> createXdgToplevelSurface(KWayland::Client::Surface *surface);
std::unique_ptr<XdgToplevel> createXdgToplevelSurface(KWayland::Client::Surface *surface, CreationSetup configureMode);
std::unique_ptr<XdgToplevel> createXdgToplevelSurface(KWayland::Client::Surface *surface, std::function<void(XdgToplevel *toplevel)> setup);
XdgToplevel *createXdgToplevelSurface(KWayland::Client::Surface *surface, QObject *parent = nullptr);
XdgToplevel *createXdgToplevelSurface(KWayland::Client::Surface *surface,
CreationSetup configureMode,
QObject *parent = nullptr);
std::unique_ptr<XdgPositioner> createXdgPositioner();
XdgPositioner *createXdgPositioner();
std::unique_ptr<XdgPopup> createXdgPopupSurface(KWayland::Client::Surface *surface, XdgSurface *parentSurface,
XdgPositioner *positioner,
CreationSetup configureMode = CreationSetup::CreateAndConfigure);
XdgPopup *createXdgPopupSurface(KWayland::Client::Surface *surface, XdgSurface *parentSurface,
XdgPositioner *positioner,
CreationSetup configureMode = CreationSetup::CreateAndConfigure,
QObject *parent = nullptr);
std::unique_ptr<XdgToplevelDecorationV1> createXdgToplevelDecorationV1(XdgToplevel *toplevel);
std::unique_ptr<IdleInhibitorV1> createIdleInhibitorV1(KWayland::Client::Surface *surface);
std::unique_ptr<AutoHideScreenEdgeV1> createAutoHideScreenEdgeV1(KWayland::Client::Surface *surface, uint32_t border);
std::unique_ptr<CursorShapeDeviceV1> createCursorShapeDeviceV1(KWayland::Client::Pointer *pointer);
std::unique_ptr<XdgDialogV1> createXdgDialogV1(XdgToplevel *toplevel);
XdgToplevelDecorationV1 *createXdgToplevelDecorationV1(XdgToplevel *toplevel, QObject *parent = nullptr);
IdleInhibitorV1 *createIdleInhibitorV1(KWayland::Client::Surface *surface);
AutoHideScreenEdgeV1 *createAutoHideScreenEdgeV1(KWayland::Client::Surface *surface, uint32_t border);
CursorShapeDeviceV1 *createCursorShapeDeviceV1(KWayland::Client::Pointer *pointer);
/**
* Creates a shared memory buffer of @p size in @p color and attaches it to the @p surface.
@ -797,14 +767,13 @@ bool renderNodeAvailable();
* with X on demand
*/
#if KWIN_BUILD_X11
struct XcbConnectionDeleter
{
void operator()(xcb_connection_t *pointer);
};
typedef std::unique_ptr<xcb_connection_t, XcbConnectionDeleter> XcbConnectionPtr;
XcbConnectionPtr createX11Connection();
#endif
MockInputMethod *inputMethod();
KWayland::Client::Surface *inputPanelSurface();

View File

@ -784,24 +784,25 @@ void LayerShellV1WindowTest::testScreenEdge()
QVERIFY(window);
QVERIFY(!window->isActive());
QSignalSpy hiddenChangedSpy(window, &Window::hiddenChanged);
QSignalSpy windowShowSpy(window, &Window::windowShown);
QSignalSpy windowHiddenSpy(window, &Window::windowHidden);
quint32 timestamp = 0;
// The layer surface will be hidden and shown when the screen edge is activated or deactivated.
{
screenEdge->activate();
QVERIFY(hiddenChangedSpy.wait());
QVERIFY(windowHiddenSpy.wait());
QVERIFY(!window->isShown());
screenEdge->deactivate();
QVERIFY(hiddenChangedSpy.wait());
QVERIFY(windowShowSpy.wait());
QVERIFY(window->isShown());
}
// The layer surface will be shown when the screen edge is triggered.
{
screenEdge->activate();
QVERIFY(hiddenChangedSpy.wait());
QVERIFY(windowHiddenSpy.wait());
QVERIFY(!window->isShown());
Test::pointerMotion(QPointF(640, 1023), timestamp);
@ -809,7 +810,7 @@ void LayerShellV1WindowTest::testScreenEdge()
Test::pointerMotion(QPointF(640, 1023), timestamp);
timestamp += 160;
Test::pointerMotion(QPointF(640, 512), timestamp);
QVERIFY(hiddenChangedSpy.wait());
QVERIFY(windowShowSpy.wait());
QVERIFY(window->isShown());
}
@ -817,7 +818,7 @@ void LayerShellV1WindowTest::testScreenEdge()
{
QSignalSpy approachingSpy(workspace()->screenEdges(), &ScreenEdges::approaching);
screenEdge->activate();
QVERIFY(hiddenChangedSpy.wait());
QVERIFY(windowHiddenSpy.wait());
QVERIFY(!window->isShown());
Test::pointerMotion(QPointF(640, 1020), timestamp++);
@ -826,7 +827,7 @@ void LayerShellV1WindowTest::testScreenEdge()
QVERIFY(approachingSpy.last().at(1).toReal() != 0.0);
screenEdge->deactivate();
QVERIFY(hiddenChangedSpy.wait());
QVERIFY(windowShowSpy.wait());
QVERIFY(window->isShown());
QVERIFY(approachingSpy.last().at(1).toReal() == 0.0);
@ -836,11 +837,11 @@ void LayerShellV1WindowTest::testScreenEdge()
// The layer surface will be shown when the screen edge is destroyed.
{
screenEdge->activate();
QVERIFY(hiddenChangedSpy.wait());
QVERIFY(windowHiddenSpy.wait());
QVERIFY(!window->isShown());
screenEdge.reset();
QVERIFY(hiddenChangedSpy.wait());
QVERIFY(windowShowSpy.wait());
QVERIFY(window->isShown());
}
}

View File

@ -67,19 +67,12 @@ private Q_SLOTS:
void testPointerShortcut();
void testAxisShortcut_data();
void testAxisShortcut();
void testKeyboardLockShortcut();
void testKeyboardShortcutsDisabledWhenLocked();
void testKeyboardShortcut();
void testTouch();
private:
struct WindowHandle
{
Window *window;
std::unique_ptr<KWayland::Client::Surface> surface;
std::unique_ptr<Test::XdgToplevel> shellSurface;
};
void unlock();
WindowHandle showWindow();
std::pair<Window *, std::unique_ptr<KWayland::Client::Surface>> showWindow();
KWayland::Client::ConnectionThread *m_connection = nullptr;
KWayland::Client::Compositor *m_compositor = nullptr;
KWayland::Client::Seat *m_seat = nullptr;
@ -154,7 +147,7 @@ void LockScreenTest::unlock()
}
}
LockScreenTest::WindowHandle LockScreenTest::showWindow()
std::pair<Window *, std::unique_ptr<KWayland::Client::Surface>> LockScreenTest::showWindow()
{
#define VERIFY(statement) \
if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__)) \
@ -165,8 +158,8 @@ LockScreenTest::WindowHandle LockScreenTest::showWindow()
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
VERIFY(surface.get());
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
VERIFY(shellSurface.get());
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
VERIFY(shellSurface);
// let's render
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
@ -176,7 +169,7 @@ LockScreenTest::WindowHandle LockScreenTest::showWindow()
#undef VERIFY
#undef COMPARE
return {window, std::move(surface), std::move(shellSurface)};
return {window, std::move(surface)};
}
void LockScreenTest::initTestCase()
@ -242,7 +235,7 @@ void LockScreenTest::testPointer()
QSignalSpy enteredSpy(pointer.get(), &KWayland::Client::Pointer::entered);
QSignalSpy leftSpy(pointer.get(), &KWayland::Client::Pointer::left);
auto [window, surface, shellSurface] = showWindow();
auto [window, surface] = showWindow();
QVERIFY(window);
// first move cursor into the center of the window
@ -285,7 +278,7 @@ void LockScreenTest::testPointerButton()
QSignalSpy enteredSpy(pointer.get(), &KWayland::Client::Pointer::entered);
QSignalSpy buttonChangedSpy(pointer.get(), &KWayland::Client::Pointer::buttonStateChanged);
auto [window, surface, shellSurface] = showWindow();
auto [window, surface] = showWindow();
QVERIFY(window);
// first move cursor into the center of the window
@ -324,7 +317,7 @@ void LockScreenTest::testPointerAxis()
QSignalSpy axisChangedSpy(pointer.get(), &KWayland::Client::Pointer::axisChanged);
QSignalSpy enteredSpy(pointer.get(), &KWayland::Client::Pointer::entered);
auto [window, surface, shellSurface] = showWindow();
auto [window, surface] = showWindow();
QVERIFY(window);
// first move cursor into the center of the window
@ -363,7 +356,7 @@ void LockScreenTest::testKeyboard()
QSignalSpy leftSpy(keyboard.get(), &KWayland::Client::Keyboard::left);
QSignalSpy keyChangedSpy(keyboard.get(), &KWayland::Client::Keyboard::keyChanged);
auto [window, surface, shellSurface] = showWindow();
auto [window, surface] = showWindow();
QVERIFY(window);
QVERIFY(enteredSpy.wait());
QTRY_COMPARE(enteredSpy.count(), 1);
@ -560,7 +553,7 @@ void LockScreenTest::testEffectsKeyboardAutorepeat()
void LockScreenTest::testMoveWindow()
{
auto [window, surface, shellSurface] = showWindow();
auto [window, surface] = showWindow();
QVERIFY(window);
QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
quint32 timestamp = 1;
@ -692,10 +685,7 @@ void LockScreenTest::testAxisShortcut()
#undef PERFORM
}
/**
* This test verifies that keyboard shortcuts are disabled when the screen is locked
*/
void LockScreenTest::testKeyboardShortcutsDisabledWhenLocked()
void LockScreenTest::testKeyboardShortcut()
{
#if !KWIN_BUILD_GLOBALSHORTCUTS
QSKIP("Can't test shortcuts without shortcuts");
@ -742,49 +732,12 @@ void LockScreenTest::testKeyboardShortcutsDisabledWhenLocked()
KEYRELEASE(KEY_LEFTALT);
}
/**
* This test verifies that the global keyboard shortcut to lock the screen works
*/
void LockScreenTest::testKeyboardLockShortcut()
{
#if !KWIN_BUILD_GLOBALSHORTCUTS
QSKIP("Can't test shortcuts without shortcuts");
return;
#endif
QList<QKeySequence> shortcuts = KGlobalAccel::self()->globalShortcut("ksmserver", "Lock Session");
// Verify the shortcut is Meta + L, the default
QCOMPARE(shortcuts.first().toString(), QString("Meta+L"));
// Verify the screen is not locked
QVERIFY(!waylandServer()->isScreenLocked());
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
// Trigger the shortcut
quint32 timestamp = 1;
KEYPRESS(KEY_LEFTMETA);
KEYPRESS(KEY_L);
KEYRELEASE(KEY_L);
KEYRELEASE(KEY_LEFTMETA);
// Verify the screen gets locked
QVERIFY(windowAddedSpy.wait());
Window *window = windowAddedSpy.first().first().value<Window *>();
QVERIFY(window);
QVERIFY(window->isLockScreen());
QTRY_COMPARE(ScreenLocker::KSldApp::self()->lockState(), ScreenLocker::KSldApp::Locked);
QVERIFY(waylandServer()->isScreenLocked());
UNLOCK;
}
void LockScreenTest::testTouch()
{
auto touch = m_seat->createTouch(m_seat);
QVERIFY(touch);
QVERIFY(touch->isValid());
auto [window, surface, shellSurface] = showWindow();
auto [window, surface] = showWindow();
QVERIFY(window);
QSignalSpy sequenceStartedSpy(touch, &KWayland::Client::Touch::sequenceStarted);
QSignalSpy cancelSpy(touch, &KWayland::Client::Touch::sequenceCanceled);

View File

@ -0,0 +1,376 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kwin.h>
#include "kwin_wayland_test.h"
#include "input.h"
#include "keyboard_input.h"
#include "pointer_input.h"
#include "wayland_server.h"
#include "workspace.h"
#include "xkb.h"
#include <KConfigGroup>
#include <QDBusConnection>
#include <linux/input.h>
using namespace KWin;
static const QString s_socketName = QStringLiteral("wayland_test_kwin_modifier_only_shortcut-0");
static const QString s_serviceName = QStringLiteral("org.kde.KWin.Test.ModifierOnlyShortcut");
static const QString s_path = QStringLiteral("/Test");
class ModifierOnlyShortcutTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void init();
void cleanup();
void testTrigger_data();
void testTrigger();
void testCapsLock();
void testGlobalShortcutsDisabled_data();
void testGlobalShortcutsDisabled();
};
class Target : public QObject
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.kde.KWin.Test.ModifierOnlyShortcut")
public:
Target();
~Target() override;
public Q_SLOTS:
Q_SCRIPTABLE void shortcut();
Q_SIGNALS:
void shortcutTriggered();
};
Target::Target()
: QObject()
{
QDBusConnection::sessionBus().registerService(s_serviceName);
QDBusConnection::sessionBus().registerObject(s_path, s_serviceName, this, QDBusConnection::ExportScriptableSlots);
}
Target::~Target()
{
QDBusConnection::sessionBus().unregisterObject(s_path);
QDBusConnection::sessionBus().unregisterService(s_serviceName);
}
void Target::shortcut()
{
Q_EMIT shortcutTriggered();
}
void ModifierOnlyShortcutTest::initTestCase()
{
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
QVERIFY(waylandServer()->init(s_socketName));
Test::setOutputConfig({
QRect(0, 0, 1280, 1024),
QRect(1280, 0, 1280, 1024),
});
kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig));
qputenv("KWIN_XKB_DEFAULT_KEYMAP", "1");
qputenv("XKB_DEFAULT_RULES", "evdev");
kwinApp()->start();
QVERIFY(applicationStartedSpy.wait());
}
void ModifierOnlyShortcutTest::init()
{
workspace()->setActiveOutput(QPoint(640, 512));
KWin::input()->pointer()->warp(QPoint(640, 512));
}
void ModifierOnlyShortcutTest::cleanup()
{
}
void ModifierOnlyShortcutTest::testTrigger_data()
{
QTest::addColumn<QStringList>("metaConfig");
QTest::addColumn<QStringList>("altConfig");
QTest::addColumn<QStringList>("controlConfig");
QTest::addColumn<QStringList>("shiftConfig");
QTest::addColumn<int>("modifier");
QTest::addColumn<QList<int>>("nonTriggeringMods");
const QStringList trigger = QStringList{s_serviceName, s_path, s_serviceName, QStringLiteral("shortcut")};
const QStringList e = QStringList();
QTest::newRow("leftMeta") << trigger << e << e << e << KEY_LEFTMETA << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
QTest::newRow("rightMeta") << trigger << e << e << e << KEY_RIGHTMETA << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
QTest::newRow("leftAlt") << e << trigger << e << e << KEY_LEFTALT << QList<int>{KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
QTest::newRow("rightAlt") << e << trigger << e << e << KEY_RIGHTALT << QList<int>{KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
QTest::newRow("leftControl") << e << e << trigger << e << KEY_LEFTCTRL << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
QTest::newRow("rightControl") << e << e << trigger << e << KEY_RIGHTCTRL << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
QTest::newRow("leftShift") << e << e << e << trigger << KEY_LEFTSHIFT << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA, KEY_RIGHTMETA};
QTest::newRow("rightShift") << e << e << e << trigger << KEY_RIGHTSHIFT << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA, KEY_RIGHTMETA};
}
void ModifierOnlyShortcutTest::testTrigger()
{
// this test verifies that modifier only shortcut triggers correctly
Target target;
QSignalSpy triggeredSpy(&target, &Target::shortcutTriggered);
KConfigGroup group = kwinApp()->config()->group(QStringLiteral("ModifierOnlyShortcuts"));
QFETCH(QStringList, metaConfig);
QFETCH(QStringList, altConfig);
QFETCH(QStringList, shiftConfig);
QFETCH(QStringList, controlConfig);
group.writeEntry("Meta", metaConfig);
group.writeEntry("Alt", altConfig);
group.writeEntry("Shift", shiftConfig);
group.writeEntry("Control", controlConfig);
group.sync();
workspace()->slotReconfigure();
// configured shortcut should trigger
quint32 timestamp = 1;
QFETCH(int, modifier);
Test::keyboardKeyPressed(modifier, timestamp++);
Test::keyboardKeyReleased(modifier, timestamp++);
QCOMPARE(triggeredSpy.count(), 1);
// the other shortcuts should not trigger
QFETCH(QList<int>, nonTriggeringMods);
for (auto it = nonTriggeringMods.constBegin(), end = nonTriggeringMods.constEnd(); it != end; it++) {
Test::keyboardKeyPressed(*it, timestamp++);
Test::keyboardKeyReleased(*it, timestamp++);
QCOMPARE(triggeredSpy.count(), 1);
}
// try configured again
Test::keyboardKeyPressed(modifier, timestamp++);
Test::keyboardKeyReleased(modifier, timestamp++);
QCOMPARE(triggeredSpy.count(), 2);
// click another key while modifier is held
Test::keyboardKeyPressed(modifier, timestamp++);
Test::keyboardKeyPressed(KEY_A, timestamp++);
Test::keyboardKeyReleased(KEY_A, timestamp++);
Test::keyboardKeyReleased(modifier, timestamp++);
QCOMPARE(triggeredSpy.count(), 2);
// release other key after modifier release
Test::keyboardKeyPressed(modifier, timestamp++);
Test::keyboardKeyPressed(KEY_A, timestamp++);
Test::keyboardKeyReleased(modifier, timestamp++);
Test::keyboardKeyReleased(KEY_A, timestamp++);
QCOMPARE(triggeredSpy.count(), 2);
// press key before pressing modifier
Test::keyboardKeyPressed(KEY_A, timestamp++);
Test::keyboardKeyPressed(modifier, timestamp++);
Test::keyboardKeyReleased(modifier, timestamp++);
Test::keyboardKeyReleased(KEY_A, timestamp++);
QCOMPARE(triggeredSpy.count(), 2);
// mouse button pressed before clicking modifier
Test::pointerButtonPressed(BTN_LEFT, timestamp++);
QCOMPARE(input()->qtButtonStates(), Qt::LeftButton);
Test::keyboardKeyPressed(modifier, timestamp++);
Test::keyboardKeyReleased(modifier, timestamp++);
Test::pointerButtonReleased(BTN_LEFT, timestamp++);
QCOMPARE(input()->qtButtonStates(), Qt::NoButton);
QCOMPARE(triggeredSpy.count(), 2);
// mouse button press before mod press, release before mod release
Test::pointerButtonPressed(BTN_LEFT, timestamp++);
QCOMPARE(input()->qtButtonStates(), Qt::LeftButton);
Test::keyboardKeyPressed(modifier, timestamp++);
Test::pointerButtonReleased(BTN_LEFT, timestamp++);
Test::keyboardKeyReleased(modifier, timestamp++);
QCOMPARE(input()->qtButtonStates(), Qt::NoButton);
QCOMPARE(triggeredSpy.count(), 2);
// mouse button click while mod is pressed
Test::keyboardKeyPressed(modifier, timestamp++);
Test::pointerButtonPressed(BTN_LEFT, timestamp++);
QCOMPARE(input()->qtButtonStates(), Qt::LeftButton);
Test::pointerButtonReleased(BTN_LEFT, timestamp++);
Test::keyboardKeyReleased(modifier, timestamp++);
QCOMPARE(input()->qtButtonStates(), Qt::NoButton);
QCOMPARE(triggeredSpy.count(), 2);
// scroll while mod is pressed
Test::keyboardKeyPressed(modifier, timestamp++);
Test::pointerAxisVertical(5.0, timestamp++);
Test::keyboardKeyReleased(modifier, timestamp++);
QCOMPARE(triggeredSpy.count(), 2);
// same for horizontal
Test::keyboardKeyPressed(modifier, timestamp++);
Test::pointerAxisHorizontal(5.0, timestamp++);
Test::keyboardKeyReleased(modifier, timestamp++);
QCOMPARE(triggeredSpy.count(), 2);
#if KWIN_BUILD_SCREENLOCKER
// now try to lock the screen while modifier key is pressed
Test::keyboardKeyPressed(modifier, timestamp++);
QVERIFY(Test::lockScreen());
Test::keyboardKeyReleased(modifier, timestamp++);
QCOMPARE(triggeredSpy.count(), 2);
// now trigger while screen is locked, should also not work
Test::keyboardKeyPressed(modifier, timestamp++);
Test::keyboardKeyReleased(modifier, timestamp++);
QCOMPARE(triggeredSpy.count(), 2);
QVERIFY(Test::unlockScreen());
#endif
}
void ModifierOnlyShortcutTest::testCapsLock()
{
// this test verifies that Capslock does not trigger the shift shortcut
// but other shortcuts still trigger even when Capslock is on
Target target;
QSignalSpy triggeredSpy(&target, &Target::shortcutTriggered);
KConfigGroup group = kwinApp()->config()->group(QStringLiteral("ModifierOnlyShortcuts"));
group.writeEntry("Meta", QStringList());
group.writeEntry("Alt", QStringList());
group.writeEntry("Shift", QStringList{s_serviceName, s_path, s_serviceName, QStringLiteral("shortcut")});
group.writeEntry("Control", QStringList());
group.sync();
workspace()->slotReconfigure();
// first test that the normal shortcut triggers
quint32 timestamp = 1;
const int modifier = KEY_LEFTSHIFT;
Test::keyboardKeyPressed(modifier, timestamp++);
Test::keyboardKeyReleased(modifier, timestamp++);
QCOMPARE(triggeredSpy.count(), 1);
// now capslock
Test::keyboardKeyPressed(KEY_CAPSLOCK, timestamp++);
Test::keyboardKeyReleased(KEY_CAPSLOCK, timestamp++);
QCOMPARE(input()->keyboardModifiers(), Qt::NoModifier);
QCOMPARE(triggeredSpy.count(), 1);
// currently caps lock is on
// shift still triggers
Test::keyboardKeyPressed(modifier, timestamp++);
Test::keyboardKeyReleased(modifier, timestamp++);
QCOMPARE(input()->keyboardModifiers(), Qt::NoModifier);
QCOMPARE(triggeredSpy.count(), 2);
// meta should also trigger
group.writeEntry("Meta", QStringList{s_serviceName, s_path, s_serviceName, QStringLiteral("shortcut")});
group.writeEntry("Alt", QStringList());
group.writeEntry("Shift", QStringList{});
group.writeEntry("Control", QStringList());
group.sync();
workspace()->slotReconfigure();
Test::keyboardKeyPressed(KEY_LEFTMETA, timestamp++);
QCOMPARE(input()->keyboardModifiers(), Qt::MetaModifier);
QCOMPARE(input()->keyboard()->xkb()->modifiersRelevantForGlobalShortcuts(), Qt::MetaModifier);
Test::keyboardKeyReleased(KEY_LEFTMETA, timestamp++);
QCOMPARE(triggeredSpy.count(), 3);
// set back to shift to ensure we don't trigger with capslock
group.writeEntry("Meta", QStringList());
group.writeEntry("Alt", QStringList());
group.writeEntry("Shift", QStringList{s_serviceName, s_path, s_serviceName, QStringLiteral("shortcut")});
group.writeEntry("Control", QStringList());
group.sync();
workspace()->slotReconfigure();
// release caps lock
Test::keyboardKeyPressed(KEY_CAPSLOCK, timestamp++);
Test::keyboardKeyReleased(KEY_CAPSLOCK, timestamp++);
QCOMPARE(input()->keyboardModifiers(), Qt::NoModifier);
QCOMPARE(triggeredSpy.count(), 3);
}
void ModifierOnlyShortcutTest::testGlobalShortcutsDisabled_data()
{
QTest::addColumn<QStringList>("metaConfig");
QTest::addColumn<QStringList>("altConfig");
QTest::addColumn<QStringList>("controlConfig");
QTest::addColumn<QStringList>("shiftConfig");
QTest::addColumn<int>("modifier");
const QStringList trigger = QStringList{s_serviceName, s_path, s_serviceName, QStringLiteral("shortcut")};
const QStringList e = QStringList();
QTest::newRow("leftMeta") << trigger << e << e << e << KEY_LEFTMETA;
QTest::newRow("rightMeta") << trigger << e << e << e << KEY_RIGHTMETA;
QTest::newRow("leftAlt") << e << trigger << e << e << KEY_LEFTALT;
QTest::newRow("rightAlt") << e << trigger << e << e << KEY_RIGHTALT;
QTest::newRow("leftControl") << e << e << trigger << e << KEY_LEFTCTRL;
QTest::newRow("rightControl") << e << e << trigger << e << KEY_RIGHTCTRL;
QTest::newRow("leftShift") << e << e << e << trigger << KEY_LEFTSHIFT;
QTest::newRow("rightShift") << e << e << e << trigger << KEY_RIGHTSHIFT;
}
void ModifierOnlyShortcutTest::testGlobalShortcutsDisabled()
{
// this test verifies that when global shortcuts are disabled inside KWin (e.g. through a window rule)
// the modifier only shortcuts do not trigger.
// see BUG: 370146
Target target;
QSignalSpy triggeredSpy(&target, &Target::shortcutTriggered);
KConfigGroup group = kwinApp()->config()->group(QStringLiteral("ModifierOnlyShortcuts"));
QFETCH(QStringList, metaConfig);
QFETCH(QStringList, altConfig);
QFETCH(QStringList, shiftConfig);
QFETCH(QStringList, controlConfig);
group.writeEntry("Meta", metaConfig);
group.writeEntry("Alt", altConfig);
group.writeEntry("Shift", shiftConfig);
group.writeEntry("Control", controlConfig);
group.sync();
workspace()->slotReconfigure();
// trigger once to verify the shortcut works
quint32 timestamp = 1;
QFETCH(int, modifier);
QVERIFY(!workspace()->globalShortcutsDisabled());
Test::keyboardKeyPressed(modifier, timestamp++);
Test::keyboardKeyReleased(modifier, timestamp++);
QCOMPARE(triggeredSpy.count(), 1);
triggeredSpy.clear();
// now disable global shortcuts
workspace()->disableGlobalShortcutsForClient(true);
QVERIFY(workspace()->globalShortcutsDisabled());
// Should not get triggered
Test::keyboardKeyPressed(modifier, timestamp++);
Test::keyboardKeyReleased(modifier, timestamp++);
QCOMPARE(triggeredSpy.count(), 0);
triggeredSpy.clear();
// enable again
workspace()->disableGlobalShortcutsForClient(false);
QVERIFY(!workspace()->globalShortcutsDisabled());
// should get triggered again
Test::keyboardKeyPressed(modifier, timestamp++);
Test::keyboardKeyReleased(modifier, timestamp++);
QCOMPARE(triggeredSpy.count(), 1);
}
WAYLANDTEST_MAIN(ModifierOnlyShortcutTest)
#include "modifier_only_shortcut_test.moc"

View File

@ -54,6 +54,11 @@ private Q_SLOTS:
void testPointerMoveEnd_data();
void testPointerMoveEnd();
void testClientSideMove();
void testNetMove();
void testAdjustClientGeometryOfHiddenX11Panel_data();
void testAdjustClientGeometryOfHiddenX11Panel();
void testAdjustClientGeometryOfHiddenWaylandPanel_data();
void testAdjustClientGeometryOfHiddenWaylandPanel();
void testResizeForVirtualKeyboard_data();
void testResizeForVirtualKeyboard();
void testResizeForVirtualKeyboardWithMaximize();
@ -531,6 +536,234 @@ void MoveResizeWindowTest::testClientSideMove()
QCOMPARE(pointerEnteredSpy.last().last().toPoint(), QPoint(50, 25));
}
void MoveResizeWindowTest::testNetMove()
{
// this test verifies that a move request for an X11 window through NET API works
// create an xcb window
Test::XcbConnectionPtr c = Test::createX11Connection();
QVERIFY(!xcb_connection_has_error(c.get()));
xcb_window_t windowId = xcb_generate_id(c.get());
xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
0, 0, 100, 100,
0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
xcb_size_hints_t hints;
memset(&hints, 0, sizeof(hints));
xcb_icccm_size_hints_set_position(&hints, 1, 0, 0);
xcb_icccm_size_hints_set_size(&hints, 1, 100, 100);
xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
// let's set a no-border
NETWinInfo winInfo(c.get(), windowId, rootWindow(), NET::WMWindowType, NET::Properties2());
winInfo.setWindowType(NET::Override);
xcb_map_window(c.get(), windowId);
xcb_flush(c.get());
QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
QVERIFY(windowCreatedSpy.wait());
X11Window *window = windowCreatedSpy.first().first().value<X11Window *>();
QVERIFY(window);
QCOMPARE(window->window(), windowId);
const QRectF origGeo = window->frameGeometry();
// let's move the cursor outside the window
input()->pointer()->warp(workspace()->activeOutput()->geometry().center());
QVERIFY(!exclusiveContains(origGeo, Cursors::self()->mouse()->pos()));
QSignalSpy interactiveMoveResizeStartedSpy(window, &X11Window::interactiveMoveResizeStarted);
QSignalSpy interactiveMoveResizeFinishedSpy(window, &X11Window::interactiveMoveResizeFinished);
QSignalSpy interactiveMoveResizeSteppedSpy(window, &X11Window::interactiveMoveResizeStepped);
QVERIFY(!workspace()->moveResizeWindow());
// use NETRootInfo to trigger a move request
NETRootInfo root(c.get(), NET::Properties());
root.moveResizeRequest(windowId, origGeo.center().x(), origGeo.center().y(), NET::Move, XCB_BUTTON_INDEX_1);
xcb_flush(c.get());
QVERIFY(interactiveMoveResizeStartedSpy.wait());
QCOMPARE(workspace()->moveResizeWindow(), window);
QVERIFY(window->isInteractiveMove());
QCOMPARE(window->geometryRestore(), origGeo);
QCOMPARE(Cursors::self()->mouse()->pos(), origGeo.center());
// let's move a step
input()->pointer()->warp(Cursors::self()->mouse()->pos() + QPoint(10, 10));
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
QCOMPARE(interactiveMoveResizeSteppedSpy.first().last(), origGeo.translated(10, 10));
// let's cancel the move resize again through the net API
root.moveResizeRequest(windowId, window->frameGeometry().center().x(), window->frameGeometry().center().y(), NET::MoveResizeCancel, XCB_BUTTON_INDEX_1);
xcb_flush(c.get());
QVERIFY(interactiveMoveResizeFinishedSpy.wait());
// and destroy the window again
xcb_unmap_window(c.get(), windowId);
xcb_destroy_window(c.get(), windowId);
xcb_flush(c.get());
c.reset();
QSignalSpy windowClosedSpy(window, &X11Window::closed);
QVERIFY(windowClosedSpy.wait());
}
void MoveResizeWindowTest::testAdjustClientGeometryOfHiddenX11Panel_data()
{
QTest::addColumn<QRect>("panelGeometry");
QTest::addColumn<QPoint>("targetPoint");
QTest::addColumn<QPoint>("expectedAdjustedPoint");
QTest::addColumn<quint32>("hideLocation");
QTest::newRow("top") << QRect(0, 0, 100, 20) << QPoint(50, 25) << QPoint(50, 20) << 0u;
QTest::newRow("bottom") << QRect(0, 1024 - 20, 100, 20) << QPoint(50, 1024 - 25 - 50) << QPoint(50, 1024 - 20 - 50) << 2u;
QTest::newRow("left") << QRect(0, 0, 20, 100) << QPoint(25, 50) << QPoint(20, 50) << 3u;
QTest::newRow("right") << QRect(1280 - 20, 0, 20, 100) << QPoint(1280 - 25 - 100, 50) << QPoint(1280 - 20 - 100, 50) << 1u;
}
void MoveResizeWindowTest::testAdjustClientGeometryOfHiddenX11Panel()
{
// this test verifies that auto hiding panels are ignored when adjusting client geometry
// see BUG 365892
// first create our panel
Test::XcbConnectionPtr c = Test::createX11Connection();
QVERIFY(!xcb_connection_has_error(c.get()));
xcb_window_t windowId = xcb_generate_id(c.get());
QFETCH(QRect, panelGeometry);
xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
panelGeometry.x(), panelGeometry.y(), panelGeometry.width(), panelGeometry.height(),
0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
xcb_size_hints_t hints;
memset(&hints, 0, sizeof(hints));
xcb_icccm_size_hints_set_position(&hints, 1, panelGeometry.x(), panelGeometry.y());
xcb_icccm_size_hints_set_size(&hints, 1, panelGeometry.width(), panelGeometry.height());
xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
NETWinInfo winInfo(c.get(), windowId, rootWindow(), NET::WMWindowType, NET::Properties2());
winInfo.setWindowType(NET::Dock);
xcb_map_window(c.get(), windowId);
xcb_flush(c.get());
QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
QVERIFY(windowCreatedSpy.wait());
X11Window *panel = windowCreatedSpy.first().first().value<X11Window *>();
QVERIFY(panel);
QCOMPARE(panel->window(), windowId);
QCOMPARE(panel->frameGeometry(), panelGeometry);
QVERIFY(panel->isDock());
// let's create a window
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
QVERIFY(surface != nullptr);
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
QVERIFY(shellSurface != nullptr);
auto testWindow = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
QVERIFY(testWindow);
QVERIFY(testWindow->isMovable());
// panel is not yet hidden, we should snap against it
QFETCH(QPoint, targetPoint);
QTEST(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false).toPoint(), "expectedAdjustedPoint");
// now let's hide the panel
QSignalSpy panelHiddenSpy(panel, &Window::windowHidden);
QFETCH(quint32, hideLocation);
xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId, atoms->kde_screen_edge_show, XCB_ATOM_CARDINAL, 32, 1, &hideLocation);
xcb_flush(c.get());
QVERIFY(panelHiddenSpy.wait());
// now try to snap again
QCOMPARE(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false), targetPoint);
// and destroy the panel again
xcb_unmap_window(c.get(), windowId);
xcb_destroy_window(c.get(), windowId);
xcb_flush(c.get());
c.reset();
QSignalSpy panelClosedSpy(panel, &X11Window::closed);
QVERIFY(panelClosedSpy.wait());
// snap once more
QCOMPARE(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false), targetPoint);
// and close
QSignalSpy windowClosedSpy(testWindow, &Window::closed);
shellSurface.reset();
surface.reset();
QVERIFY(windowClosedSpy.wait());
}
void MoveResizeWindowTest::testAdjustClientGeometryOfHiddenWaylandPanel_data()
{
QTest::addColumn<uint32_t>("anchor");
QTest::addColumn<QRect>("panelGeometry");
QTest::addColumn<QPoint>("targetPoint");
QTest::addColumn<QPoint>("expectedAdjustedPoint");
QTest::newRow("top") << uint32_t(Test::LayerSurfaceV1::anchor_top) << QRect(0, 0, 1280, 20) << QPoint(50, 25) << QPoint(50, 20);
QTest::newRow("bottom") << uint32_t(Test::LayerSurfaceV1::anchor_bottom) << QRect(0, 1024 - 20, 1280, 20) << QPoint(50, 1024 - 25 - 50) << QPoint(50, 1024 - 20 - 50);
QTest::newRow("left") << uint32_t(Test::LayerSurfaceV1::anchor_left) << QRect(0, 0, 20, 1024) << QPoint(25, 50) << QPoint(20, 50);
QTest::newRow("right") << uint32_t(Test::LayerSurfaceV1::anchor_right) << QRect(1280 - 20, 0, 20, 1024) << QPoint(1280 - 25 - 100, 50) << QPoint(1280 - 20 - 100, 50);
}
void MoveResizeWindowTest::testAdjustClientGeometryOfHiddenWaylandPanel()
{
// this test verifies that hidden panels are ignored when adjusting client geometry
// see BUG 365892
// first create our panel
std::unique_ptr<KWayland::Client::Surface> panelSurface(Test::createSurface());
std::unique_ptr<Test::LayerSurfaceV1> panelShellSurface(Test::createLayerSurfaceV1(panelSurface.get(), QStringLiteral("dock")));
QFETCH(QRect, panelGeometry);
QFETCH(uint32_t, anchor);
panelShellSurface->set_anchor(anchor);
panelShellSurface->set_size(panelGeometry.width(), panelGeometry.height());
panelSurface->commit(KWayland::Client::Surface::CommitFlag::None);
// let's render
QSignalSpy panelConfigureRequestedSpy(panelShellSurface.get(), &Test::LayerSurfaceV1::configureRequested);
QVERIFY(panelConfigureRequestedSpy.wait());
auto panel = Test::renderAndWaitForShown(panelSurface.get(), panelConfigureRequestedSpy.last().at(1).toSize(), Qt::blue);
QVERIFY(panel);
QCOMPARE(panel->frameGeometry(), panelGeometry);
QVERIFY(panel->isDock());
// let's create a window
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
QVERIFY(surface != nullptr);
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
QVERIFY(shellSurface != nullptr);
auto testWindow = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
QVERIFY(testWindow);
QVERIFY(testWindow->isMovable());
// panel is not yet hidden, we should snap against it
QFETCH(QPoint, targetPoint);
QTEST(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false).toPoint(), "expectedAdjustedPoint");
// now let's hide the panel
panel->setHidden(true);
// now try to snap again
QCOMPARE(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false), targetPoint);
// and destroy the panel again
QSignalSpy panelClosedSpy(panel, &Window::closed);
panelShellSurface.reset();
panelSurface.reset();
QVERIFY(panelClosedSpy.wait());
// snap once more
QCOMPARE(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false), targetPoint);
// and close
QSignalSpy windowClosedSpy(testWindow, &Window::closed);
shellSurface.reset();
surface.reset();
QVERIFY(windowClosedSpy.wait());
}
void MoveResizeWindowTest::testResizeForVirtualKeyboard_data()
{
QTest::addColumn<QRect>("windowRect");
@ -806,12 +1039,6 @@ void MoveResizeWindowTest::testCancelInteractiveMoveResize()
QVERIFY(shellSurface != nullptr);
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
QVERIFY(window);
QSignalSpy frameGeomtryChangedSpy(window, &Window::frameGeometryChanged);
QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged);
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
QVERIFY(surfaceConfigureRequestedSpy.wait());
// tile / maximize window
QFETCH(QuickTileMode, quickTileMode);
@ -821,20 +1048,21 @@ void MoveResizeWindowTest::testCancelInteractiveMoveResize()
} else {
window->setQuickTileMode(quickTileMode, true);
}
QCOMPARE(window->requestedQuickTileMode(), quickTileMode);
QCOMPARE(window->quickTileMode(), quickTileMode);
QCOMPARE(window->requestedMaximizeMode(), maximizeMode);
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
QVERIFY(surfaceConfigureRequestedSpy.wait());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue);
QVERIFY(frameGeomtryChangedSpy.wait());
QCOMPARE(window->quickTileMode(), quickTileMode);
const QRectF geometry = window->moveResizeGeometry();
const QRectF geometryRestore = window->geometryRestore();
// Start resizing the client.
QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
QCOMPARE(workspace()->moveResizeWindow(), nullptr);
QCOMPARE(window->isInteractiveMove(), false);
QCOMPARE(window->isInteractiveResize(), false);
@ -844,22 +1072,12 @@ void MoveResizeWindowTest::testCancelInteractiveMoveResize()
QCOMPARE(window->isInteractiveMove(), false);
QCOMPARE(window->isInteractiveResize(), true);
Test::pointerMotionRelative(QPoint(-10, -10), 1);
QVERIFY(surfaceConfigureRequestedSpy.wait());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue);
QVERIFY(frameGeomtryChangedSpy.wait());
Test::pointerMotionRelative(QPoint(1, 1), 1);
QCOMPARE(window->quickTileMode(), QuickTileMode());
QCOMPARE(window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
// cancel moveresize, all state from before should be restored
window->keyPressEvent(Qt::Key::Key_Escape);
QVERIFY(surfaceConfigureRequestedSpy.wait());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue);
QVERIFY(frameGeomtryChangedSpy.wait());
QCOMPARE(window->moveResizeGeometry(), geometry);
QCOMPARE(window->quickTileMode(), quickTileMode);
QCOMPARE(window->requestedMaximizeMode(), maximizeMode);

View File

@ -26,6 +26,8 @@
using namespace KWin;
static const QString s_socketName = QStringLiteral("wayland_test_kwin_no_global_shortcuts-0");
static const QString s_serviceName = QStringLiteral("org.kde.KWin.Test.ModifierOnlyShortcut");
static const QString s_path = QStringLiteral("/Test");
Q_DECLARE_METATYPE(KWin::ElectricBorder)
@ -40,6 +42,8 @@ private Q_SLOTS:
void init();
void cleanup();
void testTrigger_data();
void testTrigger();
void testKGlobalAccel();
void testPointerShortcut();
void testAxisShortcut_data();
@ -50,6 +54,7 @@ private Q_SLOTS:
class Target : public QObject
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.kde.KWin.Test.ModifierOnlyShortcut")
public:
Target();
@ -65,10 +70,14 @@ Q_SIGNALS:
Target::Target()
: QObject()
{
QDBusConnection::sessionBus().registerService(s_serviceName);
QDBusConnection::sessionBus().registerObject(s_path, s_serviceName, this, QDBusConnection::ExportScriptableSlots);
}
Target::~Target()
{
QDBusConnection::sessionBus().unregisterObject(s_path);
QDBusConnection::sessionBus().unregisterService(s_serviceName);
}
void Target::shortcut()
@ -80,8 +89,7 @@ void NoGlobalShortcutsTest::initTestCase()
{
qRegisterMetaType<KWin::ElectricBorder>("ElectricBorder");
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
kwinApp()->setSupportsGlobalShortcuts(false);
QVERIFY(waylandServer()->init(s_socketName));
QVERIFY(waylandServer()->init(s_socketName, KWin::WaylandServer::InitializationFlag::NoGlobalShortcuts));
Test::setOutputConfig({
QRect(0, 0, 1280, 1024),
QRect(1280, 0, 1280, 1024),
@ -105,6 +113,62 @@ void NoGlobalShortcutsTest::cleanup()
{
}
void NoGlobalShortcutsTest::testTrigger_data()
{
QTest::addColumn<QStringList>("metaConfig");
QTest::addColumn<QStringList>("altConfig");
QTest::addColumn<QStringList>("controlConfig");
QTest::addColumn<QStringList>("shiftConfig");
QTest::addColumn<int>("modifier");
QTest::addColumn<QList<int>>("nonTriggeringMods");
const QStringList trigger = QStringList{s_serviceName, s_path, s_serviceName, QStringLiteral("shortcut")};
const QStringList e = QStringList();
QTest::newRow("leftMeta") << trigger << e << e << e << KEY_LEFTMETA << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
QTest::newRow("rightMeta") << trigger << e << e << e << KEY_RIGHTMETA << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
QTest::newRow("leftAlt") << e << trigger << e << e << KEY_LEFTALT << QList<int>{KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
QTest::newRow("rightAlt") << e << trigger << e << e << KEY_RIGHTALT << QList<int>{KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
QTest::newRow("leftControl") << e << e << trigger << e << KEY_LEFTCTRL << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
QTest::newRow("rightControl") << e << e << trigger << e << KEY_RIGHTCTRL << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
QTest::newRow("leftShift") << e << e << e << trigger << KEY_LEFTSHIFT << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA, KEY_RIGHTMETA};
QTest::newRow("rightShift") << e << e << e << trigger << KEY_RIGHTSHIFT << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA, KEY_RIGHTMETA};
}
void NoGlobalShortcutsTest::testTrigger()
{
// test based on ModifierOnlyShortcutTest::testTrigger
Target target;
QSignalSpy triggeredSpy(&target, &Target::shortcutTriggered);
KConfigGroup group = kwinApp()->config()->group(QStringLiteral("ModifierOnlyShortcuts"));
QFETCH(QStringList, metaConfig);
QFETCH(QStringList, altConfig);
QFETCH(QStringList, shiftConfig);
QFETCH(QStringList, controlConfig);
group.writeEntry("Meta", metaConfig);
group.writeEntry("Alt", altConfig);
group.writeEntry("Shift", shiftConfig);
group.writeEntry("Control", controlConfig);
group.sync();
workspace()->slotReconfigure();
// configured shortcut should trigger
quint32 timestamp = 1;
QFETCH(int, modifier);
Test::keyboardKeyPressed(modifier, timestamp++);
Test::keyboardKeyReleased(modifier, timestamp++);
QCOMPARE(triggeredSpy.count(), 0);
// the other shortcuts should not trigger
QFETCH(QList<int>, nonTriggeringMods);
for (auto it = nonTriggeringMods.constBegin(), end = nonTriggeringMods.constEnd(); it != end; it++) {
Test::keyboardKeyPressed(*it, timestamp++);
Test::keyboardKeyReleased(*it, timestamp++);
QCOMPARE(triggeredSpy.count(), 0);
}
}
void NoGlobalShortcutsTest::testKGlobalAccel()
{
std::unique_ptr<QAction> action(new QAction(nullptr));

View File

@ -10,15 +10,11 @@
#include "core/outputbackend.h"
#include "core/outputconfiguration.h"
#include "pointer_input.h"
#include "tiles/tilemanager.h"
#include "wayland_server.h"
#include "window.h"
#include "workspace.h"
#include "x11window.h"
#include <KWayland/Client/surface.h>
#include <netwm.h>
#include <xcb/xcb_icccm.h>
using namespace std::chrono_literals;
@ -45,16 +41,9 @@ private Q_SLOTS:
void testWindowRestoredAfterEnablingOutput();
void testMaximizedWindowRestoredAfterEnablingOutput();
void testFullScreenWindowRestoredAfterEnablingOutput();
void testQuickTiledWindowRestoredAfterEnablingOutput();
void testCustomTiledWindowRestoredAfterEnablingOutput_data();
void testCustomTiledWindowRestoredAfterEnablingOutput();
void testWindowRestoredAfterChangingScale();
void testMaximizeStateRestoredAfterEnablingOutput_data();
void testMaximizeStateRestoredAfterEnablingOutput();
void testInvalidGeometryRestoreAfterEnablingOutput();
void testMaximizedWindowDoesntDisappear_data();
void testMaximizedWindowDoesntDisappear();
void testXwaylandScaleChange();
void testWindowNotRestoredAfterMovingWindowAndEnablingOutput();
void testLaptopLidClosed();
@ -470,182 +459,6 @@ void OutputChangesTest::testFullScreenWindowRestoredAfterEnablingOutput()
QCOMPARE(window->fullscreenGeometryRestore(), QRectF(1280 + 50, 100, 100, 50));
}
void OutputChangesTest::testQuickTiledWindowRestoredAfterEnablingOutput()
{
// This test verifies that a quick tiled window will be moved to
// its original output and tile when the output is re-enabled
const auto outputs = kwinApp()->outputBackend()->outputs();
// Create a window.
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
QVERIFY(window);
// kwin will send a configure event with the actived state.
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
QVERIFY(surfaceConfigureRequestedSpy.wait());
// Move the window to the right monitor and tile it to the right.
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
window->move(QPointF(1280 + 50, 100));
window->setQuickTileMode(QuickTileFlag::Right, true);
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), QSize(1280 / 2, 1024));
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), QSize(1280 / 2, 1024), Qt::blue);
QVERIFY(frameGeometryChangedSpy.wait());
const QRectF rightQuickTileGeom = QRectF(1280 + 1280 / 2, 0, 1280 / 2, 1024);
QCOMPARE(window->frameGeometry(), rightQuickTileGeom);
QCOMPARE(window->moveResizeGeometry(), rightQuickTileGeom);
QCOMPARE(window->output(), outputs[1]);
QCOMPARE(window->quickTileMode(), QuickTileFlag::Right);
QCOMPARE(window->requestedQuickTileMode(), QuickTileFlag::Right);
QCOMPARE(window->geometryRestore(), QRectF(1280 + 50, 100, 100, 50));
// Disable the right output.
OutputConfiguration config1;
{
auto changeSet = config1.changeSet(outputs[1]);
changeSet->enabled = false;
}
workspace()->applyOutputConfiguration(config1);
// The window will be moved to the left monitor
QCOMPARE(window->output(), outputs[0]);
// Enable the right monitor again
OutputConfiguration config2;
{
auto changeSet = config2.changeSet(outputs[1]);
changeSet->enabled = true;
}
workspace()->applyOutputConfiguration(config2);
// The window will be moved back to the right monitor, and put in the correct tile
QCOMPARE(window->frameGeometry(), rightQuickTileGeom);
QCOMPARE(window->moveResizeGeometry(), rightQuickTileGeom);
QCOMPARE(window->output(), outputs[1]);
QCOMPARE(window->quickTileMode(), QuickTileFlag::Right);
QCOMPARE(window->requestedQuickTileMode(), QuickTileFlag::Right);
QCOMPARE(window->geometryRestore(), QRectF(1280 + 50, 100, 100, 50));
}
void OutputChangesTest::testCustomTiledWindowRestoredAfterEnablingOutput_data()
{
const auto outputs = kwinApp()->outputBackend()->outputs();
const size_t tileCount = workspace()->tileManager(outputs[1])->rootTile()->childTiles().size();
QTest::addColumn<size_t>("tileIndex");
for (size_t i = 0; i < tileCount; i++) {
QTest::addRow("tile %lu", i) << i;
}
}
void OutputChangesTest::testCustomTiledWindowRestoredAfterEnablingOutput()
{
// This test verifies that a custom tiled window will be moved to
// its original output and tile when the output is re-enabled
const auto outputs = kwinApp()->outputBackend()->outputs();
// start with only one output
{
OutputConfiguration config;
auto changeSet = config.changeSet(outputs[1]);
changeSet->enabled = false;
workspace()->applyOutputConfiguration(config);
}
// Create a window.
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
const auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
QVERIFY(window);
// kwin will send a configure event with the actived state.
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
QVERIFY(surfaceConfigureRequestedSpy.wait());
const QRectF originalGeometry = window->moveResizeGeometry();
// Enable the right output
{
OutputConfiguration config;
auto changeSet = config.changeSet(outputs[1]);
changeSet->enabled = true;
workspace()->applyOutputConfiguration(config);
}
QFETCH(size_t, tileIndex);
const QRectF customTileGeom = workspace()->tileManager(outputs[1])->rootTile()->childTiles()[tileIndex]->windowGeometry();
// Move the window to the right monitor and put it in the middle tile.
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
window->move(customTileGeom.topLeft() + QPointF(50, 50));
const auto geomBeforeTiling = window->moveResizeGeometry();
window->setQuickTileMode(QuickTileFlag::Custom, true);
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), customTileGeom.size().toSize());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), customTileGeom.size().toSize(), Qt::blue);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->frameGeometry(), customTileGeom);
QCOMPARE(window->moveResizeGeometry(), customTileGeom);
QCOMPARE(window->output(), outputs[1]);
QCOMPARE(window->quickTileMode(), QuickTileFlag::Custom);
QCOMPARE(window->requestedQuickTileMode(), QuickTileFlag::Custom);
QCOMPARE(window->geometryRestore(), geomBeforeTiling);
// Disable the right output.
{
OutputConfiguration config;
auto changeSet = config.changeSet(outputs[1]);
changeSet->enabled = false;
workspace()->applyOutputConfiguration(config);
}
// The window will be moved to the left monitor, and the original geometry restored
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), originalGeometry.size().toSize());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), originalGeometry.size().toSize(), Qt::blue);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->frameGeometry(), originalGeometry);
QCOMPARE(window->moveResizeGeometry(), originalGeometry);
QCOMPARE(window->output(), outputs[0]);
QCOMPARE(window->quickTileMode(), QuickTileFlag::None);
QCOMPARE(window->requestedQuickTileMode(), QuickTileFlag::None);
// Enable the right monitor again
{
OutputConfiguration config;
auto changeSet = config.changeSet(outputs[1]);
changeSet->enabled = true;
workspace()->applyOutputConfiguration(config);
}
// The window will be moved back to the right monitor, and put in the correct tile
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), customTileGeom.size().toSize());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), customTileGeom.size().toSize(), Qt::blue);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->frameGeometry(), customTileGeom);
QCOMPARE(window->moveResizeGeometry(), customTileGeom);
QCOMPARE(window->output(), outputs[1]);
QCOMPARE(window->quickTileMode(), QuickTileFlag::Custom);
QCOMPARE(window->requestedQuickTileMode(), QuickTileFlag::Custom);
QCOMPARE(window->geometryRestore(), geomBeforeTiling);
}
void OutputChangesTest::testWindowRestoredAfterChangingScale()
{
// This test verifies that a window will be moved to its original position after changing the scale of an output
@ -689,21 +502,11 @@ void OutputChangesTest::testWindowRestoredAfterChangingScale()
QCOMPARE(window->output(), output);
}
void OutputChangesTest::testMaximizeStateRestoredAfterEnablingOutput_data()
{
QTest::addColumn<MaximizeMode>("maximizeMode");
QTest::addRow("Vertical Maximization") << MaximizeMode::MaximizeVertical;
QTest::addRow("Horizontal Maximization") << MaximizeMode::MaximizeHorizontal;
QTest::addRow("Full Maximization") << MaximizeMode::MaximizeFull;
}
void OutputChangesTest::testMaximizeStateRestoredAfterEnablingOutput()
{
// This test verifies that the window state will get restored after disabling and enabling an output,
// even if its maximize state changed in the process
QFETCH(MaximizeMode, maximizeMode);
const auto outputs = kwinApp()->outputBackend()->outputs();
// Disable the right output
@ -738,16 +541,17 @@ void OutputChangesTest::testMaximizeStateRestoredAfterEnablingOutput()
// Move the window to the right monitor and make it maximized.
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
window->move(QPointF(1280 + 50, 100));
window->maximize(maximizeMode);
window->maximize(MaximizeFull);
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), QSize(1280, 1024));
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::blue);
Test::render(surface.get(), QSize(1280, 1024), Qt::blue);
QVERIFY(frameGeometryChangedSpy.wait());
const auto maximizedGeometry = window->moveResizeGeometry();
QCOMPARE(window->frameGeometry(), maximizedGeometry);
QCOMPARE(window->frameGeometry(), QRectF(1280, 0, 1280, 1024));
QCOMPARE(window->moveResizeGeometry(), QRectF(1280, 0, 1280, 1024));
QCOMPARE(window->output(), outputs[1]);
QCOMPARE(window->maximizeMode(), maximizeMode);
QCOMPARE(window->requestedMaximizeMode(), maximizeMode);
QCOMPARE(window->maximizeMode(), MaximizeFull);
QCOMPARE(window->requestedMaximizeMode(), MaximizeFull);
QCOMPARE(window->geometryRestore(), QRectF(1280 + 50, 100, 100, 50));
// Disable the right output
@ -780,15 +584,15 @@ void OutputChangesTest::testMaximizeStateRestoredAfterEnablingOutput()
// The window will be moved back to the right monitor, maximized and the geometry restore will be updated
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), maximizedGeometry.size());
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), outputs[1]->geometry().size());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::blue);
Test::render(surface.get(), outputs[1]->geometry().size(), Qt::blue);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->frameGeometry(), maximizedGeometry);
QCOMPARE(window->moveResizeGeometry(), maximizedGeometry);
QCOMPARE(window->frameGeometry(), QRectF(1280, 0, 1280, 1024));
QCOMPARE(window->moveResizeGeometry(), QRectF(1280, 0, 1280, 1024));
QCOMPARE(window->output(), outputs[1]);
QCOMPARE(window->maximizeMode(), maximizeMode);
QCOMPARE(window->requestedMaximizeMode(), maximizeMode);
QCOMPARE(window->maximizeMode(), MaximizeFull);
QCOMPARE(window->requestedMaximizeMode(), MaximizeFull);
QCOMPARE(window->geometryRestore(), QRectF(1280 + 50, 100, 100, 50));
}
@ -885,90 +689,6 @@ void OutputChangesTest::testInvalidGeometryRestoreAfterEnablingOutput()
QCOMPARE(window->geometryRestore(), rightGeometryRestore);
}
void OutputChangesTest::testMaximizedWindowDoesntDisappear_data()
{
QTest::addColumn<MaximizeMode>("maximizeMode");
QTest::addRow("Vertical Maximization") << MaximizeMode::MaximizeVertical;
QTest::addRow("Horizontal Maximization") << MaximizeMode::MaximizeHorizontal;
QTest::addRow("Full Maximization") << MaximizeMode::MaximizeFull;
}
void OutputChangesTest::testMaximizedWindowDoesntDisappear()
{
// This test verifies that (vertically, horizontally) maximized windows don't get placed out of the screen
// when the output they're on gets disabled or removed
Test::setOutputConfig({
Test::OutputInfo{
.geometry = QRect(5120 / 3, 1440, 2256 / 1.3, 1504 / 1.3),
.scale = 1.3,
.internal = true,
},
Test::OutputInfo{
.geometry = QRect(0, 0, 5120, 1440),
.scale = 1,
.internal = false,
},
});
const auto outputs = kwinApp()->outputBackend()->outputs();
QFETCH(MaximizeMode, maximizeMode);
workspace()->setActiveOutput(outputs[1]);
// Create a window.
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
auto window = Test::renderAndWaitForShown(surface.get(), QSize(500, 300), Qt::blue);
QVERIFY(window);
// kwin will send a configure event with the actived state.
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
QVERIFY(surfaceConfigureRequestedSpy.wait());
window->move(outputs[1]->geometry().topLeft() + QPoint(3500, 500));
const QRectF originalGeometry = window->frameGeometry();
QVERIFY(outputs[1]->geometryF().contains(originalGeometry));
// vertically maximize the window
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
window->maximize(maximizeMode);
QVERIFY(surfaceConfigureRequestedSpy.wait());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::blue);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->output(), outputs[1]);
const auto maximizedGeometry = window->moveResizeGeometry();
QCOMPARE(window->frameGeometry(), maximizedGeometry);
QCOMPARE(window->maximizeMode(), maximizeMode);
QCOMPARE(window->requestedMaximizeMode(), maximizeMode);
QCOMPARE(window->geometryRestore(), originalGeometry);
// Disable the top output
{
OutputConfiguration config;
auto changeSet0 = config.changeSet(outputs[0]);
changeSet0->pos = QPoint(0, 0);
auto changeSet = config.changeSet(outputs[1]);
changeSet->enabled = false;
workspace()->applyOutputConfiguration(config);
}
// The window should be moved to the left output
QVERIFY(surfaceConfigureRequestedSpy.wait());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::blue);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->output(), outputs[0]);
QVERIFY(outputs[0]->geometryF().contains(window->frameGeometry()));
QVERIFY(outputs[0]->geometryF().contains(window->moveResizeGeometry()));
QCOMPARE(window->maximizeMode(), maximizeMode);
QCOMPARE(window->requestedMaximizeMode(), maximizeMode);
}
void OutputChangesTest::testLaptopLidClosed()
{
Test::setOutputConfig({
@ -1008,80 +728,6 @@ void OutputChangesTest::testLaptopLidClosed()
input()->removeInputDevice(lidSwitch.get());
}
static X11Window *createX11Window(xcb_connection_t *connection, const QRect &geometry, std::function<void(xcb_window_t)> setup = {})
{
xcb_window_t windowId = xcb_generate_id(connection);
xcb_create_window(connection, XCB_COPY_FROM_PARENT, windowId, rootWindow(),
geometry.x(),
geometry.y(),
geometry.width(),
geometry.height(),
0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
xcb_size_hints_t hints;
memset(&hints, 0, sizeof(hints));
xcb_icccm_size_hints_set_position(&hints, 1, geometry.x(), geometry.y());
xcb_icccm_size_hints_set_size(&hints, 1, geometry.width(), geometry.height());
xcb_icccm_set_wm_normal_hints(connection, windowId, &hints);
if (setup) {
setup(windowId);
}
xcb_map_window(connection, windowId);
xcb_flush(connection);
QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
if (!windowCreatedSpy.wait()) {
return nullptr;
}
return windowCreatedSpy.last().first().value<X11Window *>();
}
void OutputChangesTest::testXwaylandScaleChange()
{
Test::setOutputConfig({
QRect(0, 0, 1280, 1024),
QRect(1280, 0, 1280, 1024),
});
const auto outputs = workspace()->outputs();
{
OutputConfiguration config;
config.changeSet(outputs[0])->scale = 2;
config.changeSet(outputs[1])->scale = 1;
workspace()->applyOutputConfiguration(config);
}
QCOMPARE(kwinApp()->xwaylandScale(), 2);
Test::XcbConnectionPtr c = Test::createX11Connection();
QVERIFY(!xcb_connection_has_error(c.get()));
X11Window *window = createX11Window(c.get(), QRect(0, 0, 100, 200));
const QRectF originalGeometry = window->frameGeometry();
// disable the left output -> window gets moved to the right output
{
OutputConfiguration config;
config.changeSet(outputs[0])->enabled = false;
workspace()->applyOutputConfiguration(config);
}
// the window should still have logical size of 100, 200
QCOMPARE(kwinApp()->xwaylandScale(), 1);
QCOMPARE(window->frameGeometry().size(), originalGeometry.size());
// enable the left output again
{
OutputConfiguration config;
config.changeSet(outputs[0])->enabled = true;
workspace()->applyOutputConfiguration(config);
}
// the window should be back in its original geometry
QCOMPARE(kwinApp()->xwaylandScale(), 2);
QCOMPARE(window->frameGeometry(), originalGeometry);
}
} // namespace KWin
WAYLANDTEST_MAIN(KWin::OutputChangesTest)

View File

@ -0,0 +1,146 @@
/*
SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-FileContributor: Jan Grulich <jgrulich@redhat.com>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "pipewirecore_p.h"
#include <QDebug>
#include <QSocketNotifier>
#include <QThread>
#include <QThreadStorage>
#include <mutex>
#include <spa/utils/result.h>
pw_core_events PipeWireCore::s_pwCoreEvents = {
.version = PW_VERSION_CORE_EVENTS,
.info = &PipeWireCore::onCoreInfo,
.done = nullptr,
.ping = nullptr,
.error = &PipeWireCore::onCoreError,
.remove_id = nullptr,
.bound_id = nullptr,
.add_mem = nullptr,
.remove_mem = nullptr,
};
PipeWireCore::PipeWireCore()
{
static std::once_flag pwInitOnce;
std::call_once(pwInitOnce, [] {
pw_init(nullptr, nullptr);
});
}
void PipeWireCore::onCoreError(void *data, uint32_t id, int seq, int res, const char *message)
{
Q_UNUSED(seq)
qWarning() << "PipeWire remote error: " << res << message;
if (id == PW_ID_CORE) {
PipeWireCore *pw = static_cast<PipeWireCore *>(data);
Q_EMIT pw->pipewireFailed(QString::fromUtf8(message));
if (res == -EPIPE) {
// Broken pipe, need reconnecting
if (pw->m_pwCore) {
Q_EMIT pw->pipeBroken();
spa_hook_remove(&pw->m_coreListener);
pw_core_disconnect(pw->m_pwCore);
pw->init_core();
}
}
}
}
void PipeWireCore::onCoreInfo(void *data, const struct pw_core_info *info)
{
PipeWireCore *pw = static_cast<PipeWireCore *>(data);
pw->m_serverVersion = QVersionNumber::fromString(QString::fromUtf8(info->version));
}
PipeWireCore::~PipeWireCore()
{
if (m_pwMainLoop) {
pw_loop_leave(m_pwMainLoop);
}
if (m_pwCore) {
pw_core_disconnect(m_pwCore);
}
if (m_pwContext) {
pw_context_destroy(m_pwContext);
}
if (m_pwMainLoop) {
pw_loop_destroy(m_pwMainLoop);
}
}
bool PipeWireCore::init(int fd)
{
m_pwMainLoop = pw_loop_new(nullptr);
pw_loop_enter(m_pwMainLoop);
QSocketNotifier *notifier = new QSocketNotifier(pw_loop_get_fd(m_pwMainLoop), QSocketNotifier::Read, this);
connect(notifier, &QSocketNotifier::activated, this, [this] {
int result = pw_loop_iterate(m_pwMainLoop, 0);
if (result < 0)
qWarning() << "pipewire_loop_iterate failed: " << spa_strerror(result);
});
m_pwContext = pw_context_new(m_pwMainLoop, nullptr, 0);
if (!m_pwContext) {
qWarning() << "Failed to create PipeWire context";
m_error = QStringLiteral("Failed to create PipeWire context");
return false;
}
m_fd = fd;
return init_core();
}
bool PipeWireCore::init_core()
{
if (m_fd > 0) {
m_pwCore = pw_context_connect_fd(m_pwContext, m_fd, nullptr, 0);
} else {
m_pwCore = pw_context_connect(m_pwContext, nullptr, 0);
}
if (!m_pwCore) {
m_error = QStringLiteral("Failed to connect to PipeWire");
qWarning() << "error:" << m_error << m_fd;
return false;
}
if (pw_loop_iterate(m_pwMainLoop, 0) < 0) {
qWarning() << "Failed to start main PipeWire loop";
m_error = QStringLiteral("Failed to start main PipeWire loop");
return false;
}
pw_core_add_listener(m_pwCore, &m_coreListener, &s_pwCoreEvents, this);
return true;
}
QSharedPointer<PipeWireCore> PipeWireCore::fetch(int fd)
{
static QThreadStorage<QHash<int, QWeakPointer<PipeWireCore>>> global;
QSharedPointer<PipeWireCore> ret = global.localData().value(fd).toStrongRef();
if (!ret) {
ret.reset(new PipeWireCore);
if (ret->init(fd)) {
global.localData().insert(fd, ret);
}
}
return ret;
}
QString PipeWireCore::error() const
{
return m_error;
}

View File

@ -0,0 +1,62 @@
/*
SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-FileContributor: Jan Grulich <jgrulich@redhat.com>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include <QObject>
#include <QVersionNumber>
#include <pipewire/pipewire.h>
class PipeWireCore : public QObject
{
Q_OBJECT
public:
PipeWireCore();
static void onCoreError(void *data, uint32_t id, int seq, int res, const char *message);
static void onCoreInfo(void *data, const struct pw_core_info *info);
~PipeWireCore();
bool init(int fd);
bool init_core();
QString error() const;
QVersionNumber serverVersion() const
{
return m_serverVersion;
}
pw_loop *loop() const
{
return m_pwMainLoop;
}
pw_core *operator*() const
{
return m_pwCore;
};
static QSharedPointer<PipeWireCore> fetch(int fd);
private:
int m_fd = 0;
pw_core *m_pwCore = nullptr;
pw_context *m_pwContext = nullptr;
pw_loop *m_pwMainLoop = nullptr;
spa_hook m_coreListener;
QString m_error;
QVersionNumber m_serverVersion;
static pw_core_events s_pwCoreEvents;
Q_SIGNALS:
void pipewireFailed(const QString &message);
/**
* Clients should disconnect from the core and reconnect to it on receiving this signal
*/
void pipeBroken();
};

View File

@ -0,0 +1,643 @@
/*
SPDX-FileCopyrightText: 2018-2020 Red Hat Inc
SPDX-FileCopyrightText: 2020-2021 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-FileContributor: Jan Grulich <jgrulich@redhat.com>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include <epoxy/egl.h>
#include "pipewirecore_p.h"
#include "pipewiresourcestream.h"
#include <libdrm/drm_fourcc.h>
#include <spa/utils/result.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <QGuiApplication>
#include <QOpenGLTexture>
#include <QSocketNotifier>
#include <QThread>
#include <QVersionNumber>
#include <qpa/qplatformnativeinterface.h>
#undef Status
#if !PW_CHECK_VERSION(0, 3, 29)
#define SPA_POD_PROP_FLAG_MANDATORY (1u << 3)
#endif
#if !PW_CHECK_VERSION(0, 3, 33)
#define SPA_POD_PROP_FLAG_DONT_FIXATE (1u << 4)
#endif
#define CURSOR_BPP 4
#define CURSOR_META_SIZE(w, h) (sizeof(struct spa_meta_cursor) + sizeof(struct spa_meta_bitmap) + w * h * CURSOR_BPP)
pw_stream_events pwStreamEvents = {};
static QImage SpaBufferToQImage(const uchar *data, int width, int height, qsizetype bytesPerLine, spa_video_format format)
{
switch (format) {
case SPA_VIDEO_FORMAT_BGRx:
case SPA_VIDEO_FORMAT_BGRA:
case SPA_VIDEO_FORMAT_xBGR:
case SPA_VIDEO_FORMAT_ABGR: {
// This is needed because QImage does not support BGRA
// This is obviously a much slower path, it makes sense to avoid it as much as possible
return QImage(data, width, height, bytesPerLine, SpaToQImageFormat(format)).rgbSwapped();
}
case SPA_VIDEO_FORMAT_BGR:
case SPA_VIDEO_FORMAT_RGBx:
case SPA_VIDEO_FORMAT_RGB:
case SPA_VIDEO_FORMAT_RGBA:
default:
return QImage(data, width, height, bytesPerLine, SpaToQImageFormat(format));
}
}
QImage::Format SpaToQImageFormat(quint32 format)
{
switch (format) {
case SPA_VIDEO_FORMAT_BGRx:
case SPA_VIDEO_FORMAT_BGRA:
return QImage::Format_RGBA8888_Premultiplied; // Handled in SpaBufferToQImage
case SPA_VIDEO_FORMAT_ABGR:
case SPA_VIDEO_FORMAT_xBGR:
return QImage::Format_ARGB32; // Handled in SpaBufferToQImage
case SPA_VIDEO_FORMAT_BGR:
return QImage::Format_BGR888;
case SPA_VIDEO_FORMAT_RGBx:
return QImage::Format_RGBX8888;
case SPA_VIDEO_FORMAT_RGB:
return QImage::Format_RGB888;
case SPA_VIDEO_FORMAT_RGBA:
return QImage::Format_RGBA8888_Premultiplied;
default:
qWarning() << "cannot convert spa format to QImage" << format;
return QImage::Format_RGB32;
}
}
struct PipeWireSourceStreamPrivate
{
QSharedPointer<PipeWireCore> pwCore;
pw_stream *pwStream = nullptr;
spa_hook streamListener;
uint32_t pwNodeId = 0;
std::optional<std::chrono::nanoseconds> m_currentPresentationTimestamp;
QAtomicInt m_stopped = false;
pw_stream_state m_state = PW_STREAM_STATE_UNCONNECTED;
spa_video_info_raw videoFormat;
QString m_error;
bool m_allowDmaBuf = true;
bool m_usingDmaBuf = false;
QHash<spa_video_format, QList<uint64_t>> m_availableModifiers;
spa_source *m_renegotiateEvent = nullptr;
bool m_withDamage = false;
Fraction maxFramerate;
};
static const QVersionNumber pwClientVersion = QVersionNumber::fromString(QString::fromUtf8(pw_get_library_version()));
static const QVersionNumber kDmaBufMinVersion = {0, 3, 24};
static const QVersionNumber kDmaBufModifierMinVersion = {0, 3, 33};
static const QVersionNumber kDropSingleModifierMinVersion = {0, 3, 40};
uint32_t PipeWireSourceStream::spaVideoFormatToDrmFormat(spa_video_format spa_format)
{
switch (spa_format) {
case SPA_VIDEO_FORMAT_RGBA:
return DRM_FORMAT_ABGR8888;
case SPA_VIDEO_FORMAT_RGBx:
return DRM_FORMAT_XBGR8888;
case SPA_VIDEO_FORMAT_BGRA:
return DRM_FORMAT_ARGB8888;
case SPA_VIDEO_FORMAT_BGRx:
return DRM_FORMAT_XRGB8888;
case SPA_VIDEO_FORMAT_BGR:
return DRM_FORMAT_BGR888;
case SPA_VIDEO_FORMAT_RGB:
return DRM_FORMAT_RGB888;
case SPA_VIDEO_FORMAT_xBGR:
return DRM_FORMAT_RGBX8888;
case SPA_VIDEO_FORMAT_ABGR:
return DRM_FORMAT_RGBA8888;
default:
qWarning() << "cannot convert spa format to fourcc" << spa_format;
return DRM_FORMAT_INVALID;
}
}
static QHash<spa_video_format, QList<uint64_t>> queryDmaBufModifiers(EGLDisplay display, const QList<spa_video_format> &formats)
{
QHash<spa_video_format, QList<uint64_t>> ret;
ret.reserve(formats.size());
const bool hasEglImageDmaBufImportExt = epoxy_has_egl_extension(display, "EGL_EXT_image_dma_buf_import");
static auto eglQueryDmaBufModifiersEXT = (PFNEGLQUERYDMABUFMODIFIERSEXTPROC)eglGetProcAddress("eglQueryDmaBufModifiersEXT");
static auto eglQueryDmaBufFormatsEXT = (PFNEGLQUERYDMABUFFORMATSEXTPROC)eglGetProcAddress("eglQueryDmaBufFormatsEXT");
EGLint count = 0;
EGLBoolean successFormats = eglQueryDmaBufFormatsEXT(display, 0, nullptr, &count);
QList<uint32_t> drmFormats(count);
successFormats &= eglQueryDmaBufFormatsEXT(display, count, reinterpret_cast<EGLint *>(drmFormats.data()), &count);
if (!successFormats)
qWarning() << "Failed to query DMA-BUF formats.";
const QList<uint64_t> mods = hasEglImageDmaBufImportExt ? QList<uint64_t>{DRM_FORMAT_MOD_INVALID} : QList<uint64_t>{};
if (!eglQueryDmaBufFormatsEXT || !eglQueryDmaBufModifiersEXT || !hasEglImageDmaBufImportExt || !successFormats) {
for (spa_video_format format : formats) {
ret[format] = mods;
}
return ret;
}
for (spa_video_format format : formats) {
uint32_t drm_format = PipeWireSourceStream::spaVideoFormatToDrmFormat(format);
if (drm_format == DRM_FORMAT_INVALID) {
qDebug() << "Failed to find matching DRM format." << format;
break;
}
if (std::find(drmFormats.begin(), drmFormats.end(), drm_format) == drmFormats.end()) {
qDebug() << "Format " << drm_format << " not supported for modifiers.";
ret[format] = mods;
break;
}
successFormats = eglQueryDmaBufModifiersEXT(display, drm_format, 0, nullptr, nullptr, &count);
if (!successFormats) {
qWarning() << "Failed to query DMA-BUF modifier count.";
ret[format] = mods;
break;
}
QList<uint64_t> modifiers(count);
if (count > 0) {
if (!eglQueryDmaBufModifiersEXT(display, drm_format, count, modifiers.data(), nullptr, &count)) {
qWarning() << "Failed to query DMA-BUF modifiers.";
}
}
// Support modifier-less buffers
modifiers.push_back(DRM_FORMAT_MOD_INVALID);
ret[format] = modifiers;
}
return ret;
}
void PipeWireSourceStream::onStreamStateChanged(void *data, pw_stream_state old, pw_stream_state state, const char *error_message)
{
PipeWireSourceStream *pw = static_cast<PipeWireSourceStream *>(data);
qDebug() << "state changed" << pw_stream_state_as_string(old) << "->" << pw_stream_state_as_string(state) << error_message;
pw->d->m_state = state;
Q_EMIT pw->stateChanged(state, old);
switch (state) {
case PW_STREAM_STATE_ERROR:
qWarning() << "Stream error: " << error_message;
break;
case PW_STREAM_STATE_PAUSED:
Q_EMIT pw->streamReady();
break;
case PW_STREAM_STATE_STREAMING:
Q_EMIT pw->startStreaming();
break;
case PW_STREAM_STATE_CONNECTING:
break;
case PW_STREAM_STATE_UNCONNECTED:
if (!pw->d->m_stopped) {
Q_EMIT pw->stopStreaming();
}
break;
}
}
void PipeWireSourceStream::onRenegotiate(void *data, uint64_t)
{
PipeWireSourceStream *pw = static_cast<PipeWireSourceStream *>(data);
uint8_t buffer[4096];
spa_pod_builder podBuilder = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
auto params = pw->createFormatsParams(podBuilder);
pw_stream_update_params(pw->d->pwStream, params.data(), params.size());
}
void PipeWireSourceStream::renegotiateModifierFailed(spa_video_format format, quint64 modifier)
{
if (d->pwCore->serverVersion() >= kDropSingleModifierMinVersion) {
const int removed = d->m_availableModifiers[format].removeAll(modifier);
if (removed == 0) {
d->m_allowDmaBuf = false;
}
} else {
d->m_allowDmaBuf = false;
}
qDebug() << "renegotiating, modifier didn't work" << format << modifier << "now only offering" << d->m_availableModifiers[format].count();
pw_loop_signal_event(d->pwCore->loop(), d->m_renegotiateEvent);
}
static spa_pod *
buildFormat(spa_pod_builder *builder, spa_video_format format, const QList<uint64_t> &modifiers, bool withDontFixate, const Fraction &requestedMaxFramerate)
{
spa_pod_frame f[2];
const spa_rectangle pw_min_screen_bounds{1, 1};
const spa_rectangle pw_max_screen_bounds{UINT32_MAX, UINT32_MAX};
spa_pod_builder_push_object(builder, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
spa_pod_builder_add(builder, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0);
spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0);
spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0);
spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle(&pw_min_screen_bounds, &pw_min_screen_bounds, &pw_max_screen_bounds), 0);
if (requestedMaxFramerate) {
auto defFramerate = SPA_FRACTION(0, 1);
auto minFramerate = SPA_FRACTION(1, 1);
auto maxFramerate = SPA_FRACTION(requestedMaxFramerate.numerator, requestedMaxFramerate.denominator);
spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&defFramerate), 0);
spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_CHOICE_RANGE_Fraction(&maxFramerate, &minFramerate, &maxFramerate), 0);
}
if (modifiers.size() == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID) {
// we only support implicit modifiers, use shortpath to skip fixation phase
spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY);
spa_pod_builder_long(builder, modifiers[0]);
} else if (!modifiers.isEmpty()) {
// SPA_POD_PROP_FLAG_DONT_FIXATE can be used with PipeWire >= 0.3.33
if (withDontFixate) {
spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE);
} else {
spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY);
}
spa_pod_builder_push_choice(builder, &f[1], SPA_CHOICE_Enum, 0);
// mofifiers from the array
for (auto it = modifiers.begin(); it != modifiers.end(); it++) {
spa_pod_builder_long(builder, *it);
if (it == modifiers.begin()) {
spa_pod_builder_long(builder, *it);
}
}
spa_pod_builder_pop(builder, &f[1]);
}
return static_cast<spa_pod *>(spa_pod_builder_pop(builder, &f[0]));
}
static const int videoDamageRegionCount = 16;
void PipeWireSourceStream::onStreamParamChanged(void *data, uint32_t id, const struct spa_pod *format)
{
if (!format || id != SPA_PARAM_Format) {
return;
}
PipeWireSourceStream *pw = static_cast<PipeWireSourceStream *>(data);
spa_format_video_raw_parse(format, &pw->d->videoFormat);
uint8_t paramsBuffer[1024];
spa_pod_builder pod_builder = SPA_POD_BUILDER_INIT(paramsBuffer, sizeof(paramsBuffer));
// When SPA_FORMAT_VIDEO_modifier is present we can use DMA-BUFs as
// the server announces support for it.
// See https://github.com/PipeWire/pipewire/blob/master/doc/dma-buf.dox
pw->d->m_usingDmaBuf = pw->d->m_allowDmaBuf && spa_pod_find_prop(format, nullptr, SPA_FORMAT_VIDEO_modifier);
Q_ASSERT(pw->d->m_allowDmaBuf || !pw->d->m_usingDmaBuf);
const auto bufferTypes =
pw->d->m_usingDmaBuf ? (1 << SPA_DATA_DmaBuf) | (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr) : (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr);
QVarLengthArray<const spa_pod *> params = {
(spa_pod *)spa_pod_builder_add_object(&pod_builder,
SPA_TYPE_OBJECT_ParamBuffers,
SPA_PARAM_Buffers,
SPA_PARAM_BUFFERS_buffers,
SPA_POD_CHOICE_RANGE_Int(16, 2, 16),
SPA_PARAM_BUFFERS_align,
SPA_POD_Int(16),
SPA_PARAM_BUFFERS_dataType,
SPA_POD_CHOICE_FLAGS_Int(bufferTypes)),
(spa_pod *)spa_pod_builder_add_object(&pod_builder,
SPA_TYPE_OBJECT_ParamMeta,
SPA_PARAM_Meta,
SPA_PARAM_META_type,
SPA_POD_Id(SPA_META_Header),
SPA_PARAM_META_size,
SPA_POD_Int(sizeof(struct spa_meta_header))),
(spa_pod *)spa_pod_builder_add_object(&pod_builder,
SPA_TYPE_OBJECT_ParamMeta,
SPA_PARAM_Meta,
SPA_PARAM_META_type,
SPA_POD_Id(SPA_META_Cursor),
SPA_PARAM_META_size,
SPA_POD_CHOICE_RANGE_Int(CURSOR_META_SIZE(64, 64), CURSOR_META_SIZE(1, 1), CURSOR_META_SIZE(1024, 1024))),
};
if (pw->d->m_withDamage) {
params.append((spa_pod *)spa_pod_builder_add_object(&pod_builder,
SPA_TYPE_OBJECT_ParamMeta,
SPA_PARAM_Meta,
SPA_PARAM_META_type,
SPA_POD_Id(SPA_META_VideoDamage),
SPA_PARAM_META_size,
SPA_POD_CHOICE_RANGE_Int(sizeof(struct spa_meta_region) * videoDamageRegionCount,
sizeof(struct spa_meta_region) * 1,
sizeof(struct spa_meta_region) * videoDamageRegionCount)));
}
pw_stream_update_params(pw->d->pwStream, params.data(), params.count());
Q_EMIT pw->streamParametersChanged();
}
static void onProcess(void *data)
{
PipeWireSourceStream *stream = static_cast<PipeWireSourceStream *>(data);
stream->process();
}
QSize PipeWireSourceStream::size() const
{
return QSize(d->videoFormat.size.width, d->videoFormat.size.height);
}
pw_stream_state PipeWireSourceStream::state() const
{
return d->m_state;
}
std::optional<std::chrono::nanoseconds> PipeWireSourceStream::currentPresentationTimestamp() const
{
return d->m_currentPresentationTimestamp;
}
QString PipeWireSourceStream::error() const
{
return d->m_error;
}
PipeWireSourceStream::PipeWireSourceStream(QObject *parent)
: QObject(parent)
, d(new PipeWireSourceStreamPrivate)
{
pwStreamEvents.version = PW_VERSION_STREAM_EVENTS;
pwStreamEvents.process = &onProcess;
pwStreamEvents.state_changed = &PipeWireSourceStream::onStreamStateChanged;
pwStreamEvents.param_changed = &PipeWireSourceStream::onStreamParamChanged;
}
PipeWireSourceStream::~PipeWireSourceStream()
{
d->m_stopped = true;
if (d->m_renegotiateEvent) {
pw_loop_destroy_source(d->pwCore->loop(), d->m_renegotiateEvent);
}
if (d->pwStream) {
pw_stream_destroy(d->pwStream);
}
}
Fraction PipeWireSourceStream::framerate() const
{
if (d->pwStream) {
return {d->videoFormat.max_framerate.num, d->videoFormat.max_framerate.denom};
}
return {0, 1};
}
void PipeWireSourceStream::setMaxFramerate(const Fraction &framerate)
{
d->maxFramerate = framerate;
if (d->pwStream) {
pw_loop_signal_event(d->pwCore->loop(), d->m_renegotiateEvent);
}
}
uint PipeWireSourceStream::nodeId()
{
return d->pwNodeId;
}
QList<const spa_pod *> PipeWireSourceStream::createFormatsParams(spa_pod_builder podBuilder)
{
const auto pwServerVersion = d->pwCore->serverVersion();
const QList<spa_video_format> formats = {
SPA_VIDEO_FORMAT_RGBx,
SPA_VIDEO_FORMAT_RGBA,
SPA_VIDEO_FORMAT_BGRx,
SPA_VIDEO_FORMAT_BGRA,
SPA_VIDEO_FORMAT_RGB,
SPA_VIDEO_FORMAT_BGR,
SPA_VIDEO_FORMAT_xBGR,
SPA_VIDEO_FORMAT_ABGR,
};
QList<const spa_pod *> params;
params.reserve(formats.size() * 2);
const EGLDisplay display = static_cast<EGLDisplay>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("egldisplay"));
d->m_allowDmaBuf = d->m_allowDmaBuf && (pwServerVersion.isNull() || (pwClientVersion >= kDmaBufMinVersion && pwServerVersion >= kDmaBufMinVersion));
const bool withDontFixate = d->m_allowDmaBuf && (pwServerVersion.isNull() || (pwClientVersion >= kDmaBufModifierMinVersion && pwServerVersion >= kDmaBufModifierMinVersion));
if (d->m_availableModifiers.isEmpty()) {
d->m_availableModifiers = queryDmaBufModifiers(display, formats);
}
for (auto it = d->m_availableModifiers.constBegin(), itEnd = d->m_availableModifiers.constEnd(); it != itEnd; ++it) {
if (d->m_allowDmaBuf && !it->isEmpty()) {
params += buildFormat(&podBuilder, it.key(), it.value(), withDontFixate, d->maxFramerate);
}
params += buildFormat(&podBuilder, it.key(), {}, withDontFixate, d->maxFramerate);
}
return params;
}
bool PipeWireSourceStream::createStream(uint nodeid, int fd)
{
d->m_availableModifiers.clear();
d->pwCore = PipeWireCore::fetch(fd);
if (!d->pwCore->error().isEmpty()) {
qDebug() << "received error while creating the stream" << d->pwCore->error();
d->m_error = d->pwCore->error();
return false;
}
connect(d->pwCore.data(), &PipeWireCore::pipewireFailed, this, &PipeWireSourceStream::coreFailed);
if (objectName().isEmpty()) {
setObjectName(QStringLiteral("plasma-screencast-%1").arg(nodeid));
}
const auto pwServerVersion = d->pwCore->serverVersion();
d->pwStream = pw_stream_new(**d->pwCore, objectName().toUtf8().constData(), nullptr);
d->pwNodeId = nodeid;
pw_stream_add_listener(d->pwStream, &d->streamListener, &pwStreamEvents, this);
d->m_renegotiateEvent = pw_loop_add_event(d->pwCore->loop(), onRenegotiate, this);
uint8_t buffer[4096];
spa_pod_builder podBuilder = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
auto params = createFormatsParams(podBuilder);
pw_stream_flags s = (pw_stream_flags)(PW_STREAM_FLAG_DONT_RECONNECT | PW_STREAM_FLAG_AUTOCONNECT);
if (pw_stream_connect(d->pwStream, PW_DIRECTION_INPUT, d->pwNodeId, s, params.data(), params.size()) != 0) {
qWarning() << "Could not connect to stream";
pw_stream_destroy(d->pwStream);
d->pwStream = nullptr;
return false;
}
qDebug() << "created successfully" << nodeid;
return true;
}
void PipeWireSourceStream::handleFrame(struct pw_buffer *buffer)
{
spa_buffer *spaBuffer = buffer->buffer;
PipeWireFrame frame;
frame.format = d->videoFormat.format;
struct spa_meta_header *h = (struct spa_meta_header *)spa_buffer_find_meta_data(spaBuffer, SPA_META_Header, sizeof(*h));
if (h) {
d->m_currentPresentationTimestamp = std::chrono::nanoseconds(h->pts);
frame.presentationTimestamp = std::chrono::nanoseconds(h->pts);
frame.sequential = h->seq;
} else {
using namespace std::chrono;
auto now = system_clock::now();
d->m_currentPresentationTimestamp = time_point_cast<nanoseconds>(now).time_since_epoch();
frame.presentationTimestamp = d->m_currentPresentationTimestamp;
}
if (spa_meta *vd = spa_buffer_find_meta(spaBuffer, SPA_META_VideoDamage)) {
frame.damage = QRegion();
spa_meta_region *mr;
spa_meta_for_each(mr, vd)
{
*frame.damage += QRect(mr->region.position.x, mr->region.position.y, mr->region.size.width, mr->region.size.height);
}
}
{ // process cursor
struct spa_meta_cursor *cursor = static_cast<struct spa_meta_cursor *>(spa_buffer_find_meta_data(spaBuffer, SPA_META_Cursor, sizeof(*cursor)));
if (cursor && spa_meta_cursor_is_valid(cursor)) {
struct spa_meta_bitmap *bitmap = nullptr;
if (cursor->bitmap_offset)
bitmap = SPA_MEMBER(cursor, cursor->bitmap_offset, struct spa_meta_bitmap);
QImage cursorTexture;
if (bitmap && bitmap->size.width > 0 && bitmap->size.height > 0) {
const uint8_t *bitmap_data = SPA_MEMBER(bitmap, bitmap->offset, uint8_t);
cursorTexture =
SpaBufferToQImage(bitmap_data, bitmap->size.width, bitmap->size.height, bitmap->stride, spa_video_format(bitmap->format));
}
frame.cursor = {{cursor->position.x, cursor->position.y}, {cursor->hotspot.x, cursor->hotspot.y}, cursorTexture};
}
}
if (spaBuffer->datas->chunk->size == 0 || spaBuffer->datas->chunk->flags == SPA_CHUNK_FLAG_CORRUPTED) {
// do not get a frame
qDebug() << "skipping empty buffer" << spaBuffer->datas->chunk->size << spaBuffer->datas->chunk->flags;
} else if (spaBuffer->datas->type == SPA_DATA_MemFd) {
uint8_t *map =
static_cast<uint8_t *>(mmap(nullptr, spaBuffer->datas->maxsize + spaBuffer->datas->mapoffset, PROT_READ, MAP_PRIVATE, spaBuffer->datas->fd, 0));
if (map == MAP_FAILED) {
qWarning() << "Failed to mmap the memory: " << strerror(errno);
return;
}
QImage img =
SpaBufferToQImage(map, d->videoFormat.size.width, d->videoFormat.size.height, spaBuffer->datas->chunk->stride, d->videoFormat.format);
frame.image = img.copy();
munmap(map, spaBuffer->datas->maxsize + spaBuffer->datas->mapoffset);
} else if (spaBuffer->datas->type == SPA_DATA_DmaBuf) {
DmaBufAttributes attribs;
attribs.planes.reserve(spaBuffer->n_datas);
attribs.format = spaVideoFormatToDrmFormat(d->videoFormat.format);
attribs.modifier = d->videoFormat.modifier;
attribs.width = d->videoFormat.size.width;
attribs.height = d->videoFormat.size.height;
for (uint i = 0; i < spaBuffer->n_datas; ++i) {
const auto &data = spaBuffer->datas[i];
DmaBufPlane plane;
plane.fd = data.fd;
plane.stride = data.chunk->stride;
plane.offset = data.chunk->offset;
attribs.planes += plane;
}
Q_ASSERT(!attribs.planes.isEmpty());
frame.dmabuf = attribs;
} else if (spaBuffer->datas->type == SPA_DATA_MemPtr) {
frame.image = SpaBufferToQImage(static_cast<uint8_t *>(spaBuffer->datas->data),
d->videoFormat.size.width,
d->videoFormat.size.height,
spaBuffer->datas->chunk->stride,
d->videoFormat.format);
} else {
if (spaBuffer->datas->type == SPA_ID_INVALID)
qWarning() << "invalid buffer type";
else
qWarning() << "unsupported buffer type" << spaBuffer->datas->type;
QImage errorImage(200, 200, QImage::Format_ARGB32_Premultiplied);
errorImage.fill(Qt::red);
frame.image = errorImage;
}
Q_EMIT frameReceived(frame);
}
void PipeWireSourceStream::coreFailed(const QString &errorMessage)
{
qDebug() << "received error message" << errorMessage;
d->m_error = errorMessage;
Q_EMIT stopStreaming();
}
void PipeWireSourceStream::process()
{
pw_buffer *buf = pw_stream_dequeue_buffer(d->pwStream);
if (!buf) {
qDebug() << "out of buffers";
return;
}
handleFrame(buf);
pw_stream_queue_buffer(d->pwStream, buf);
}
void PipeWireSourceStream::setActive(bool active)
{
Q_ASSERT(d->pwStream);
pw_stream_set_active(d->pwStream, active);
}
void PipeWireSourceStream::setDamageEnabled(bool withDamage)
{
d->m_withDamage = withDamage;
}
bool PipeWireSourceStream::usingDmaBuf() const
{
return d->m_usingDmaBuf;
}
bool PipeWireSourceStream::allowDmaBuf() const
{
return d->m_allowDmaBuf;
}
void PipeWireSourceStream::setAllowDmaBuf(bool allowed)
{
d->m_allowDmaBuf = allowed;
}
#include "moc_pipewiresourcestream.cpp"

View File

@ -0,0 +1,142 @@
/*
SPDX-FileCopyrightText: 2018-2020 Red Hat Inc
SPDX-FileCopyrightText: 2020-2021 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-FileContributor: Jan Grulich <jgrulich@redhat.com>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include <QHash>
#include <QImage>
#include <QObject>
#include <QPoint>
#include <QSharedPointer>
#include <QSize>
#include <optional>
#include <pipewire/pipewire.h>
#include <spa/param/format-utils.h>
#include <spa/param/props.h>
#include <spa/param/video/format-utils.h>
#undef Status
class PipeWireCore;
struct gbm_device;
typedef void *EGLDisplay;
struct DmaBufPlane
{
int fd; ///< The dmabuf file descriptor
uint32_t offset; ///< The offset from the start of buffer
uint32_t stride; ///< The distance from the start of a row to the next row in bytes
};
struct DmaBufAttributes
{
int width = 0;
int height = 0;
uint32_t format = 0;
uint64_t modifier = 0; ///< The layout modifier
QList<DmaBufPlane> planes;
};
struct PipeWireCursor
{
QPoint position;
QPoint hotspot;
QImage texture;
bool operator!=(const PipeWireCursor &other) const
{
return !operator==(other);
};
bool operator==(const PipeWireCursor &other) const
{
return position == other.position && hotspot == other.hotspot && texture == other.texture;
}
};
struct PipeWireFrame
{
spa_video_format format;
std::optional<int> sequential;
std::optional<std::chrono::nanoseconds> presentationTimestamp;
std::optional<DmaBufAttributes> dmabuf;
std::optional<QImage> image;
std::optional<QRegion> damage;
std::optional<PipeWireCursor> cursor;
};
struct Fraction
{
bool operator==(const Fraction &other) const
{
return numerator == other.numerator && denominator == other.denominator;
}
explicit operator bool() const
{
return isValid();
}
bool isValid() const
{
return denominator > 0;
}
quint32 numerator = 0;
quint32 denominator = 0;
};
QImage::Format SpaToQImageFormat(quint32 /*spa_video_format*/ format);
struct PipeWireSourceStreamPrivate;
class PipeWireSourceStream : public QObject
{
Q_OBJECT
public:
explicit PipeWireSourceStream(QObject *parent = nullptr);
~PipeWireSourceStream();
Fraction framerate() const;
void setMaxFramerate(const Fraction &framerate);
uint nodeId();
QString error() const;
QSize size() const;
pw_stream_state state() const;
bool createStream(uint nodeid, int fd);
void setActive(bool active);
void setDamageEnabled(bool withDamage);
void handleFrame(struct pw_buffer *buffer);
void process();
void renegotiateModifierFailed(spa_video_format format, quint64 modifier);
std::optional<std::chrono::nanoseconds> currentPresentationTimestamp() const;
static uint32_t spaVideoFormatToDrmFormat(spa_video_format spa_format);
bool usingDmaBuf() const;
bool allowDmaBuf() const;
void setAllowDmaBuf(bool allowed);
Q_SIGNALS:
void streamReady();
void startStreaming();
void stopStreaming();
void streamParametersChanged();
void frameReceived(const PipeWireFrame &frame);
void stateChanged(pw_stream_state state, pw_stream_state oldState);
private:
static void onStreamParamChanged(void *data, uint32_t id, const struct spa_pod *format);
static void onStreamStateChanged(void *data, pw_stream_state old, pw_stream_state state, const char *error_message);
static void onRenegotiate(void *data, uint64_t);
QList<const spa_pod *> createFormatsParams(spa_pod_builder podBuilder);
void coreFailed(const QString &errorMessage);
QScopedPointer<PipeWireSourceStreamPrivate> d;
};

View File

@ -25,6 +25,13 @@ using namespace KWin;
static const QString s_socketName = QStringLiteral("wayland_test_kwin_placement-0");
struct PlaceWindowResult
{
QSizeF initiallyConfiguredSize;
Test::XdgToplevel::States initiallyConfiguredStates;
QRectF finalGeometry;
};
class TestPlacement : public QObject
{
Q_OBJECT
@ -49,24 +56,12 @@ private Q_SLOTS:
private:
void setPlacementPolicy(PlacementPolicy policy);
struct WindowHandle
{
Window *window;
std::unique_ptr<KWayland::Client::Surface> surface;
std::unique_ptr<Test::XdgToplevel> shellSurface;
};
struct PlaceWindowResult
{
QSizeF initiallyConfiguredSize;
Test::XdgToplevel::States initiallyConfiguredStates;
QRectF finalGeometry;
};
/*
* Create a window and return relevant results for testing
* defaultSize is the buffer size to use if the compositor returns an empty size in the first configure
* event.
*/
std::tuple<PlaceWindowResult, WindowHandle> createAndPlaceWindow(const QSize &defaultSize);
std::pair<PlaceWindowResult, std::unique_ptr<KWayland::Client::Surface>> createAndPlaceWindow(const QSize &defaultSize);
};
void TestPlacement::init()
@ -110,15 +105,15 @@ void TestPlacement::setPlacementPolicy(PlacementPolicy policy)
Workspace::self()->slotReconfigure();
}
std::tuple<TestPlacement::PlaceWindowResult, TestPlacement::WindowHandle> TestPlacement::createAndPlaceWindow(const QSize &defaultSize)
std::pair<PlaceWindowResult, std::unique_ptr<KWayland::Client::Surface>> TestPlacement::createAndPlaceWindow(const QSize &defaultSize)
{
PlaceWindowResult rc;
// create a new window
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly);
auto shellSurface = Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly, surface.get());
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
QSignalSpy toplevelConfigureRequestedSpy(shellSurface, &Test::XdgToplevel::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
surface->commit(KWayland::Client::Surface::CommitFlag::None);
surfaceConfigureRequestedSpy.wait();
@ -136,11 +131,7 @@ std::tuple<TestPlacement::PlaceWindowResult, TestPlacement::WindowHandle> TestPl
auto window = Test::renderAndWaitForShown(surface.get(), size.toSize(), Qt::red);
rc.finalGeometry = window->frameGeometry();
return {rc, WindowHandle{
.window = window,
.surface = std::move(surface),
.shellSurface = std::move(shellSurface),
}};
return {rc, std::move(surface)};
}
void TestPlacement::testPlaceSmart()
@ -159,11 +150,11 @@ void TestPlacement::testPlaceSmart()
setPlacementPolicy(PlacementSmart);
std::vector<WindowHandle> handles;
std::vector<std::unique_ptr<KWayland::Client::Surface>> surfaces;
for (const QRect &desiredGeometry : desiredGeometries) {
auto [windowPlacement, handle] = createAndPlaceWindow(QSize(600, 500));
handles.push_back(std::move(handle));
auto [windowPlacement, surface] = createAndPlaceWindow(QSize(600, 500));
surfaces.push_back(std::move(surface));
// smart placement shouldn't define a size on windows
QCOMPARE(windowPlacement.initiallyConfiguredSize, QSize(0, 0));
@ -190,15 +181,15 @@ void TestPlacement::testPlaceMaximized()
QVERIFY(panelConfigureRequestedSpy.wait());
Test::renderAndWaitForShown(panelSurface.get(), panelConfigureRequestedSpy.last().at(1).toSize(), Qt::blue);
std::vector<WindowHandle> handles;
std::vector<std::unique_ptr<KWayland::Client::Surface>> surfaces;
// all windows should be initially maximized with an initial configure size sent
for (int i = 0; i < 4; i++) {
auto [windowPlacement, handle] = createAndPlaceWindow(QSize(600, 500));
auto [windowPlacement, surface] = createAndPlaceWindow(QSize(600, 500));
QVERIFY(windowPlacement.initiallyConfiguredStates & Test::XdgToplevel::State::Maximized);
QCOMPARE(windowPlacement.initiallyConfiguredSize, QSize(1280, 1024 - 20));
QCOMPARE(windowPlacement.finalGeometry, QRect(0, 20, 1280, 1024 - 20)); // under the panel
handles.push_back(std::move(handle));
surfaces.push_back(std::move(surface));
}
}
@ -217,14 +208,14 @@ void TestPlacement::testPlaceMaximizedLeavesFullscreen()
QVERIFY(panelConfigureRequestedSpy.wait());
Test::renderAndWaitForShown(panelSurface.get(), panelConfigureRequestedSpy.last().at(1).toSize(), Qt::blue);
std::vector<WindowHandle> handles;
std::vector<std::unique_ptr<KWayland::Client::Surface>> surfaces;
// all windows should be initially fullscreen with an initial configure size sent, despite the policy
for (int i = 0; i < 4; i++) {
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
auto shellSurface = Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly);
auto shellSurface = Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly, surface.get());
shellSurface->set_fullscreen(nullptr);
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
QSignalSpy toplevelConfigureRequestedSpy(shellSurface, &Test::XdgToplevel::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
surface->commit(KWayland::Client::Surface::CommitFlag::None);
QVERIFY(surfaceConfigureRequestedSpy.wait());
@ -239,11 +230,7 @@ void TestPlacement::testPlaceMaximizedLeavesFullscreen()
QCOMPARE(initiallyConfiguredSize, QSize(1280, 1024));
QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024));
handles.emplace_back(WindowHandle{
.window = window,
.surface = std::move(surface),
.shellSurface = std::move(shellSurface),
});
surfaces.push_back(std::move(surface));
}
}

View File

@ -185,7 +185,7 @@ void PlasmaSurfaceTest::testOSDPlacement()
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
QVERIFY(window);
QCOMPARE(window->windowType(), WindowType::OnScreenDisplay);
QCOMPARE(window->windowType(), NET::OnScreenDisplay);
QVERIFY(window->isOnScreenDisplay());
QCOMPARE(window->frameGeometry(), QRect(1280 / 2 - 100 / 2, 2 * 1024 / 3 - 50 / 2, 100, 50));
@ -224,7 +224,7 @@ void PlasmaSurfaceTest::testOSDPlacementManualPosition()
QVERIFY(window);
QVERIFY(!window->isPlaceable());
QCOMPARE(window->windowType(), WindowType::OnScreenDisplay);
QCOMPARE(window->windowType(), NET::OnScreenDisplay);
QVERIFY(window->isOnScreenDisplay());
QCOMPARE(window->frameGeometry(), QRect(50, 70, 100, 50));
}
@ -253,7 +253,7 @@ void PlasmaSurfaceTest::testPanelActivate()
auto panel = Test::renderAndWaitForShown(surface.get(), QSize(100, 200), Qt::blue);
QVERIFY(panel);
QCOMPARE(panel->windowType(), WindowType::Dock);
QCOMPARE(panel->windowType(), NET::Dock);
QVERIFY(panel->isDock());
QFETCH(bool, active);
QCOMPARE(panel->dockWantsInput(), active);

View File

@ -38,6 +38,17 @@
namespace KWin
{
static PlatformCursorImage loadReferenceThemeCursor_helper(const KXcursorTheme &theme,
const QByteArray &name)
{
const QList<KXcursorSprite> sprites = theme.shape(name);
if (sprites.isEmpty()) {
return PlatformCursorImage();
}
return PlatformCursorImage(sprites.constFirst().data(), sprites.constFirst().hotspot());
}
static PlatformCursorImage loadReferenceThemeCursor(const QByteArray &name)
{
const Cursor *pointerCursor = Cursors::self()->mouse();
@ -47,11 +58,20 @@ static PlatformCursorImage loadReferenceThemeCursor(const QByteArray &name)
return PlatformCursorImage();
}
ShapeCursorSource source;
source.setShape(name);
source.setTheme(theme);
PlatformCursorImage platformCursorImage = loadReferenceThemeCursor_helper(theme, name);
if (!platformCursorImage.isNull()) {
return platformCursorImage;
}
return PlatformCursorImage(source.image(), source.hotspot());
const QList<QByteArray> alternativeNames = Cursor::cursorAlternativeNames(name);
for (const QByteArray &alternativeName : alternativeNames) {
platformCursorImage = loadReferenceThemeCursor_helper(theme, alternativeName);
if (!platformCursorImage.isNull()) {
break;
}
}
return platformCursorImage;
}
static PlatformCursorImage loadReferenceThemeCursor(const CursorShape &shape)
@ -94,8 +114,6 @@ private Q_SLOTS:
void testWindowUnderCursorWhileButtonPressed();
void testConfineToScreenGeometry_data();
void testConfineToScreenGeometry();
void testEdgeBarrier_data();
void testEdgeBarrier();
void testResizeCursor_data();
void testResizeCursor();
void testMoveCursor();
@ -147,11 +165,6 @@ void PointerInputTest::init()
m_compositor = Test::waylandCompositor();
m_seat = Test::waylandSeat();
auto group = kwinApp()->config()->group(QStringLiteral("EdgeBarrier"));
group.writeEntry("EdgeBarrier", 0);
group.writeEntry("CornerBarrier", false);
group.sync();
Workspace::self()->slotReconfigure();
workspace()->setActiveOutput(QPoint(640, 512));
input()->pointer()->warp(QPoint(640, 512));
}
@ -182,7 +195,7 @@ void PointerInputTest::testWarpingUpdatesFocus()
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
render(surface.get());
QVERIFY(windowAddedSpy.wait());
@ -227,7 +240,7 @@ void PointerInputTest::testWarpingGeneratesPointerMotion()
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
render(surface.get());
QVERIFY(windowAddedSpy.wait());
@ -305,7 +318,7 @@ void PointerInputTest::testUpdateFocusAfterScreenChange()
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
render(surface.get(), QSize(1280, 1024));
QVERIFY(windowAddedSpy.wait());
@ -490,7 +503,7 @@ void PointerInputTest::testModifierClickUnrestrictedMove()
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
render(surface.get());
QVERIFY(windowAddedSpy.wait());
@ -555,10 +568,10 @@ void PointerInputTest::testModifierClickUnrestrictedFullscreenMove()
// create a window
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
shellSurface->set_fullscreen(nullptr);
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
QSignalSpy toplevelConfigureRequestedSpy(shellSurface, &Test::XdgToplevel::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
QVERIFY(surfaceConfigureRequestedSpy.wait());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
@ -610,7 +623,7 @@ void PointerInputTest::testModifierClickUnrestrictedMoveGlobalShortcutsDisabled(
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
render(surface.get());
QVERIFY(windowAddedSpy.wait());
@ -681,7 +694,7 @@ void PointerInputTest::testModifierScrollOpacity()
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
render(surface.get());
QVERIFY(windowAddedSpy.wait());
@ -739,7 +752,7 @@ void PointerInputTest::testModifierScrollOpacityGlobalShortcutsDisabled()
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
render(surface.get());
QVERIFY(windowAddedSpy.wait());
@ -786,7 +799,7 @@ void PointerInputTest::testScrollAction()
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
std::unique_ptr<KWayland::Client::Surface> surface1 = Test::createSurface();
QVERIFY(surface1);
std::unique_ptr<Test::XdgToplevel> shellSurface1 = Test::createXdgToplevelSurface(surface1.get());
Test::XdgToplevel *shellSurface1 = Test::createXdgToplevelSurface(surface1.get(), surface1.get());
QVERIFY(shellSurface1);
render(surface1.get());
QVERIFY(windowAddedSpy.wait());
@ -794,7 +807,7 @@ void PointerInputTest::testScrollAction()
QVERIFY(window1);
std::unique_ptr<KWayland::Client::Surface> surface2 = Test::createSurface();
QVERIFY(surface2);
std::unique_ptr<Test::XdgToplevel> shellSurface2 = Test::createXdgToplevelSurface(surface2.get());
Test::XdgToplevel *shellSurface2 = Test::createXdgToplevelSurface(surface2.get(), surface2.get());
QVERIFY(shellSurface2);
render(surface2.get());
QVERIFY(windowAddedSpy.wait());
@ -841,7 +854,7 @@ void PointerInputTest::testFocusFollowsMouse()
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
std::unique_ptr<KWayland::Client::Surface> surface1 = Test::createSurface();
QVERIFY(surface1);
std::unique_ptr<Test::XdgToplevel> shellSurface1 = Test::createXdgToplevelSurface(surface1.get());
Test::XdgToplevel *shellSurface1 = Test::createXdgToplevelSurface(surface1.get(), surface1.get());
QVERIFY(shellSurface1);
render(surface1.get(), QSize(800, 800));
QVERIFY(windowAddedSpy.wait());
@ -849,7 +862,7 @@ void PointerInputTest::testFocusFollowsMouse()
QVERIFY(window1);
std::unique_ptr<KWayland::Client::Surface> surface2 = Test::createSurface();
QVERIFY(surface2);
std::unique_ptr<Test::XdgToplevel> shellSurface2 = Test::createXdgToplevelSurface(surface2.get());
Test::XdgToplevel *shellSurface2 = Test::createXdgToplevelSurface(surface2.get(), surface2.get());
QVERIFY(shellSurface2);
render(surface2.get(), QSize(800, 800));
QVERIFY(windowAddedSpy.wait());
@ -923,7 +936,7 @@ void PointerInputTest::testMouseActionInactiveWindow()
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
std::unique_ptr<KWayland::Client::Surface> surface1 = Test::createSurface();
QVERIFY(surface1);
std::unique_ptr<Test::XdgToplevel> shellSurface1 = Test::createXdgToplevelSurface(surface1.get());
Test::XdgToplevel *shellSurface1 = Test::createXdgToplevelSurface(surface1.get(), surface1.get());
QVERIFY(shellSurface1);
render(surface1.get(), QSize(800, 800));
QVERIFY(windowAddedSpy.wait());
@ -931,7 +944,7 @@ void PointerInputTest::testMouseActionInactiveWindow()
QVERIFY(window1);
std::unique_ptr<KWayland::Client::Surface> surface2 = Test::createSurface();
QVERIFY(surface2);
std::unique_ptr<Test::XdgToplevel> shellSurface2 = Test::createXdgToplevelSurface(surface2.get());
Test::XdgToplevel *shellSurface2 = Test::createXdgToplevelSurface(surface2.get(), surface2.get());
QVERIFY(shellSurface2);
render(surface2.get(), QSize(800, 800));
QVERIFY(windowAddedSpy.wait());
@ -1008,7 +1021,7 @@ void PointerInputTest::testMouseActionActiveWindow()
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
std::unique_ptr<KWayland::Client::Surface> surface1 = Test::createSurface();
QVERIFY(surface1);
std::unique_ptr<Test::XdgToplevel> shellSurface1 = Test::createXdgToplevelSurface(surface1.get());
Test::XdgToplevel *shellSurface1 = Test::createXdgToplevelSurface(surface1.get(), surface1.get());
QVERIFY(shellSurface1);
render(surface1.get(), QSize(800, 800));
QVERIFY(windowAddedSpy.wait());
@ -1017,7 +1030,7 @@ void PointerInputTest::testMouseActionActiveWindow()
QSignalSpy window1DestroyedSpy(window1, &QObject::destroyed);
std::unique_ptr<KWayland::Client::Surface> surface2 = Test::createSurface();
QVERIFY(surface2);
std::unique_ptr<Test::XdgToplevel> shellSurface2 = Test::createXdgToplevelSurface(surface2.get());
Test::XdgToplevel *shellSurface2 = Test::createXdgToplevelSurface(surface2.get(), surface2.get());
QVERIFY(shellSurface2);
render(surface2.get(), QSize(800, 800));
QVERIFY(windowAddedSpy.wait());
@ -1084,7 +1097,7 @@ void PointerInputTest::testCursorImage()
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
render(surface.get());
QVERIFY(windowAddedSpy.wait());
@ -1214,7 +1227,7 @@ void PointerInputTest::testEffectOverrideCursorImage()
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
render(surface.get());
QVERIFY(windowAddedSpy.wait());
@ -1279,7 +1292,7 @@ void PointerInputTest::testPopup()
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
render(surface.get());
QVERIFY(windowAddedSpy.wait());
@ -1304,9 +1317,9 @@ void PointerInputTest::testPopup()
positioner->set_gravity(Test::XdgPositioner::gravity_bottom_right);
std::unique_ptr<KWayland::Client::Surface> popupSurface = Test::createSurface();
QVERIFY(popupSurface);
std::unique_ptr<Test::XdgPopup> popupShellSurface = Test::createXdgPopupSurface(popupSurface.get(), shellSurface->xdgSurface(), positioner.get());
Test::XdgPopup *popupShellSurface = Test::createXdgPopupSurface(popupSurface.get(), shellSurface->xdgSurface(), positioner.get());
QVERIFY(popupShellSurface);
QSignalSpy doneReceivedSpy(popupShellSurface.get(), &Test::XdgPopup::doneReceived);
QSignalSpy doneReceivedSpy(popupShellSurface, &Test::XdgPopup::doneReceived);
popupShellSurface->grab(*Test::waylandSeat(), 0); // FIXME: Serial.
render(popupSurface.get(), QSize(100, 50));
QVERIFY(windowAddedSpy.wait());
@ -1385,9 +1398,9 @@ void PointerInputTest::testDecoCancelsPopup()
positioner->set_gravity(Test::XdgPositioner::gravity_bottom_right);
std::unique_ptr<KWayland::Client::Surface> popupSurface = Test::createSurface();
QVERIFY(popupSurface);
std::unique_ptr<Test::XdgPopup> popupShellSurface = Test::createXdgPopupSurface(popupSurface.get(), shellSurface->xdgSurface(), positioner.get());
Test::XdgPopup *popupShellSurface = Test::createXdgPopupSurface(popupSurface.get(), shellSurface->xdgSurface(), positioner.get());
QVERIFY(popupShellSurface);
QSignalSpy doneReceivedSpy(popupShellSurface.get(), &Test::XdgPopup::doneReceived);
QSignalSpy doneReceivedSpy(popupShellSurface, &Test::XdgPopup::doneReceived);
popupShellSurface->grab(*Test::waylandSeat(), 0); // FIXME: Serial.
auto popupWindow = Test::renderAndWaitForShown(popupSurface.get(), QSize(100, 50), Qt::red);
QVERIFY(popupWindow);
@ -1422,7 +1435,7 @@ void PointerInputTest::testWindowUnderCursorWhileButtonPressed()
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
QVERIFY(shellSurface);
render(surface.get());
QVERIFY(windowAddedSpy.wait());
@ -1445,7 +1458,7 @@ void PointerInputTest::testWindowUnderCursorWhileButtonPressed()
positioner->set_gravity(Test::XdgPositioner::gravity_bottom_right);
std::unique_ptr<KWayland::Client::Surface> popupSurface = Test::createSurface();
QVERIFY(popupSurface);
std::unique_ptr<Test::XdgPopup> popupShellSurface = Test::createXdgPopupSurface(popupSurface.get(), shellSurface->xdgSurface(), positioner.get());
Test::XdgPopup *popupShellSurface = Test::createXdgPopupSurface(popupSurface.get(), shellSurface->xdgSurface(), positioner.get());
QVERIFY(popupShellSurface);
render(popupSurface.get(), QSize(99, 49));
QVERIFY(windowAddedSpy.wait());
@ -1549,88 +1562,6 @@ void PointerInputTest::testConfineToScreenGeometry()
QCOMPARE(Cursors::self()->mouse()->pos(), expectedPos);
}
void PointerInputTest::testEdgeBarrier_data()
{
QTest::addColumn<QPoint>("startPos");
QTest::addColumn<QList<QPoint>>("movements");
QTest::addColumn<int>("targetOutputId");
QTest::addColumn<bool>("cornerBarrier");
// screen layout:
//
// +----------+----------+---------+
// | left | top | right |
// +----------+----------+---------+
// | bottom |
// +----------+
//
QTest::newRow("move right - barred") << QPoint(1270, 512) << QList<QPoint>{QPoint(20, 0)} << 0 << false;
QTest::newRow("move left - barred") << QPoint(1290, 512) << QList<QPoint>{QPoint(-20, 0)} << 1 << false;
QTest::newRow("move down - barred") << QPoint(1920, 1014) << QList<QPoint>{QPoint(0, 20)} << 1 << false;
QTest::newRow("move up - barred") << QPoint(1920, 1034) << QList<QPoint>{QPoint(0, -20)} << 3 << false;
QTest::newRow("move top-right - barred") << QPoint(2550, 1034) << QList<QPoint>{QPoint(20, -20)} << 3 << false;
QTest::newRow("move top-left - barred") << QPoint(1290, 1034) << QList<QPoint>{QPoint(-20, -20)} << 3 << false;
QTest::newRow("move bottom-right - barred") << QPoint(1270, 1014) << QList<QPoint>{QPoint(20, 20)} << 0 << false;
QTest::newRow("move bottom-left - barred") << QPoint(2570, 1014) << QList<QPoint>{QPoint(-20, 20)} << 2 << false;
QTest::newRow("move right - not barred") << QPoint(1270, 512) << QList<QPoint>{QPoint(100, 0)} << 1 << false;
QTest::newRow("move left - not barred") << QPoint(1290, 512) << QList<QPoint>{QPoint(-100, 0)} << 0 << false;
QTest::newRow("move down - not barred") << QPoint(1920, 1014) << QList<QPoint>{QPoint(0, 100)} << 3 << false;
QTest::newRow("move up - not barred") << QPoint(1920, 1034) << QList<QPoint>{QPoint(0, -100)} << 1 << false;
QTest::newRow("move top-right - not barred") << QPoint(2550, 1034) << QList<QPoint>{QPoint(100, -100)} << 2 << false;
QTest::newRow("move top-left - not barred") << QPoint(1290, 1034) << QList<QPoint>{QPoint(-100, -100)} << 0 << false;
QTest::newRow("move bottom-right - not barred") << QPoint(1270, 1014) << QList<QPoint>{QPoint(100, 100)} << 3 << false;
QTest::newRow("move bottom-left - not barred") << QPoint(2570, 1014) << QList<QPoint>{QPoint(-100, 100)} << 3 << false;
QTest::newRow("move cumulative") << QPoint(1279, 512) << QList<QPoint>{QPoint(24, 0), QPoint(24, 0)} << 1 << false;
QTest::newRow("move then idle") << QPoint(1279, 512) << QList<QPoint>{QPoint(24, 0), QPoint(0, 0), QPoint(0, 0), QPoint(3, 0)} << 0 << false;
QTest::newRow("move top-right - corner barrier") << QPoint(2550, 1034) << QList<QPoint>{QPoint(100, -100)} << 3 << true;
QTest::newRow("move top-left - corner barrier") << QPoint(1290, 1034) << QList<QPoint>{QPoint(-100, -100)} << 3 << true;
QTest::newRow("move bottom-right - corner barrier") << QPoint(1270, 1014) << QList<QPoint>{QPoint(100, 100)} << 0 << true;
QTest::newRow("move bottom-left - corner barrier") << QPoint(2570, 1014) << QList<QPoint>{QPoint(-100, 100)} << 2 << true;
}
void PointerInputTest::testEdgeBarrier()
{
// setup screen layout
const QList<QRect> geometries{
QRect(0, 0, 1280, 1024),
QRect(1280, 0, 1280, 1024),
QRect(2560, 0, 1280, 1024),
QRect(1280, 1024, 1280, 1024)};
Test::setOutputConfig(geometries);
const auto outputs = workspace()->outputs();
QCOMPARE(outputs.count(), geometries.count());
QCOMPARE(outputs[0]->geometry(), geometries.at(0));
QCOMPARE(outputs[1]->geometry(), geometries.at(1));
QCOMPARE(outputs[2]->geometry(), geometries.at(2));
QCOMPARE(outputs[3]->geometry(), geometries.at(3));
QFETCH(QPoint, startPos);
input()->pointer()->warp(startPos);
quint32 timestamp = waylandServer()->seat()->timestamp().count() + 5000;
Test::pointerMotionRelative(QPoint(0, 0), timestamp);
timestamp += 1000;
QCOMPARE(Cursors::self()->mouse()->pos(), startPos);
auto group = kwinApp()->config()->group(QStringLiteral("EdgeBarrier"));
group.writeEntry("EdgeBarrier", 25);
QFETCH(bool, cornerBarrier);
group.writeEntry("CornerBarrier", cornerBarrier);
group.sync();
workspace()->slotReconfigure();
QFETCH(QList<QPoint>, movements);
for (const auto &movement : movements) {
Test::pointerMotionRelative(movement, timestamp);
timestamp += 1000;
}
QFETCH(int, targetOutputId);
QCOMPARE(workspace()->outputAt(Cursors::self()->mouse()->pos()), workspace()->outputs().at(targetOutputId));
}
void PointerInputTest::testResizeCursor_data()
{
QTest::addColumn<Qt::Edges>("edges");

View File

@ -6,7 +6,6 @@
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "effect/globals.h"
#include "kwin_wayland_test.h"
#include "core/output.h"
@ -15,7 +14,6 @@
#include "decorations/settings.h"
#include "pointer_input.h"
#include "scripting/scripting.h"
#include "tiles/tilemanager.h"
#include "utils/common.h"
#include "wayland_server.h"
#include "window.h"
@ -174,15 +172,12 @@ void QuickTilingTest::testQuickTiling()
QFETCH(QuickTileMode, mode);
QFETCH(QRectF, expectedGeometry);
const QuickTileMode oldQuickTileMode = window->quickTileMode();
window->setQuickTileMode(mode, true);
QCOMPARE(quickTileChangedSpy.count(), 1);
// at this point the geometry did not yet change
QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
// but requested quick tile mode already changed, proper quickTileMode not yet
QCOMPARE(window->requestedQuickTileMode(), mode);
// Actual quickTileMOde didn't change yet
QCOMPARE(window->quickTileMode(), oldQuickTileMode);
// but quick tile mode already changed
QCOMPARE(window->quickTileMode(), mode);
// but we got requested a new geometry
QVERIFY(surfaceConfigureRequestedSpy.wait());
@ -196,8 +191,6 @@ void QuickTilingTest::testQuickTiling()
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(frameGeometryChangedSpy.count(), 1);
QCOMPARE(window->frameGeometry(), expectedGeometry);
QCOMPARE(quickTileChangedSpy.count(), 1);
QCOMPARE(window->quickTileMode(), mode);
// send window to other screen
QList<Output *> outputs = workspace()->outputs();
@ -207,19 +200,9 @@ void QuickTilingTest::testQuickTiling()
// quick tile should not be changed
QCOMPARE(window->quickTileMode(), mode);
QTEST(window->frameGeometry(), "secondScreen");
Tile *tile = workspace()->tileManager(outputs[1])->quickTile(mode);
QCOMPARE(window->tile(), tile);
// now try to toggle again
window->setQuickTileMode(mode, true);
QTEST(window->requestedQuickTileMode(), "expectedModeAfterToggle");
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).toSize(), Qt::red);
QVERIFY(quickTileChangedSpy.wait());
QCOMPARE(quickTileChangedSpy.count(), 2);
QTEST(window->quickTileMode(), "expectedModeAfterToggle");
}
@ -260,14 +243,13 @@ void QuickTilingTest::testQuickMaximizing()
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
QSignalSpy maximizeChangedSpy(window, &Window::maximizedChanged);
const QuickTileMode oldQuickTileMode = window->quickTileMode();
window->setQuickTileMode(QuickTileFlag::Maximize, true);
QCOMPARE(quickTileChangedSpy.count(), 1);
// at this point the geometry did not yet change
QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
// but requested quick tile mode already changed
QCOMPARE(window->requestedQuickTileMode(), QuickTileFlag::Maximize);
QCOMPARE(window->quickTileMode(), oldQuickTileMode);
// but quick tile mode already changed
QCOMPARE(window->quickTileMode(), QuickTileFlag::Maximize);
QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50));
// but we got requested a new geometry
@ -281,19 +263,19 @@ void QuickTilingTest::testQuickMaximizing()
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(frameGeometryChangedSpy.count(), 1);
QCOMPARE(quickTileChangedSpy.count(), 1);
QCOMPARE(window->quickTileMode(), QuickTileFlag::Maximize);
QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024));
QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50));
// window is now set to maximised
QCOMPARE(maximizeChangedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeFull);
QCOMPARE(window->tile(), nullptr);
// go back to quick tile none
QFETCH(QuickTileMode, mode);
window->setQuickTileMode(mode, true);
QCOMPARE(window->requestedQuickTileMode(), QuickTileMode(QuickTileFlag::None));
QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None));
QCOMPARE(quickTileChangedSpy.count(), 2);
// geometry not yet changed
QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024));
QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50));
@ -304,12 +286,10 @@ void QuickTilingTest::testQuickMaximizing()
// render again
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).toSize(), Qt::yellow);
Test::render(surface.get(), QSize(100, 50), Qt::yellow);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(frameGeometryChangedSpy.count(), 2);
QCOMPARE(quickTileChangedSpy.count(), 2);
QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None));
QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50));
QCOMPARE(maximizeChangedSpy.count(), 2);
@ -344,12 +324,6 @@ void QuickTilingTest::testQuickTilingKeyboardMove()
QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None));
QCOMPARE(window->maximizeMode(), MaximizeRestore);
// We have to receive a configure event when the window becomes active.
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged);
workspace()->performWindowOperation(window, Options::UnrestrictedMoveOp);
@ -381,11 +355,6 @@ void QuickTilingTest::testQuickTilingKeyboardMove()
QCOMPARE(Cursors::self()->mouse()->pos(), targetPos);
QVERIFY(!workspace()->moveResizeWindow());
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).toSize(), Qt::red);
QVERIFY(quickTileChangedSpy.wait());
QCOMPARE(quickTileChangedSpy.count(), 1);
QTEST(window->quickTileMode(), "expectedMode");
}
@ -437,7 +406,8 @@ void QuickTilingTest::testQuickTilingPointerMove()
Test::pointerButtonPressed(BTN_LEFT, timestamp++);
Test::pointerMotion(pointerPos, timestamp++);
Test::pointerButtonReleased(BTN_LEFT, timestamp++);
QTEST(window->requestedQuickTileMode(), "expectedMode");
QCOMPARE(quickTileChangedSpy.count(), 1);
QTEST(window->quickTileMode(), "expectedMode");
QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50));
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
@ -448,8 +418,6 @@ void QuickTilingTest::testQuickTilingPointerMove()
Test::render(surface.get(), tileSize, Qt::red);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->frameGeometry().size(), tileSize);
QCOMPARE(quickTileChangedSpy.count(), 1);
QTEST(window->quickTileMode(), "expectedMode");
// verify that geometry restore is correct after user untiles the window, but changes
// their mind and tiles the window again while still holding left button
@ -458,7 +426,8 @@ void QuickTilingTest::testQuickTilingPointerMove()
Test::pointerButtonPressed(BTN_LEFT, timestamp++); // untile the window
Test::pointerMotion(QPoint(1280, 1024) / 2, timestamp++);
QCOMPARE(window->requestedQuickTileMode(), QuickTileMode(QuickTileFlag::None));
QCOMPARE(quickTileChangedSpy.count(), 2);
QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None));
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(100, 50));
@ -468,24 +437,15 @@ void QuickTilingTest::testQuickTilingPointerMove()
Test::render(surface.get(), QSize(100, 50), Qt::red);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->frameGeometry().size(), QSize(100, 50));
QCOMPARE(quickTileChangedSpy.count(), 2);
QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None));
Test::pointerMotion(pointerPos, timestamp++); // tile the window again
Test::pointerButtonReleased(BTN_LEFT, timestamp++);
QTEST(window->requestedQuickTileMode(), "expectedMode");
QCOMPARE(quickTileChangedSpy.count(), 3);
QTEST(window->quickTileMode(), "expectedMode");
QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50));
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(surfaceConfigureRequestedSpy.count(), 4);
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), tileSize);
// attach a new image
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), QSize(100, 50), Qt::red);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->frameGeometry().size(), QSize(100, 50));
QCOMPARE(quickTileChangedSpy.count(), 3);
QTEST(window->quickTileMode(), "expectedMode");
}
void QuickTilingTest::testQuickTilingTouchMove_data()
@ -552,17 +512,11 @@ void QuickTilingTest::testQuickTilingTouchMove()
// TODO: we should test both cases with fixed fake decoration for autotests.
const bool hasBorders = Workspace::self()->decorationBridge()->settings()->borderSize() != KDecoration2::BorderSize::None;
QTEST(window->requestedQuickTileMode(), "expectedMode");
QCOMPARE(quickTileChangedSpy.count(), 1);
QTEST(window->quickTileMode(), "expectedMode");
QVERIFY(surfaceConfigureRequestedSpy.wait());
QTRY_COMPARE(surfaceConfigureRequestedSpy.count(), hasBorders ? 4 : 3);
QCOMPARE(false, toplevelConfigureRequestedSpy.last().first().toSize().isEmpty());
// attach a new image
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).toSize(), Qt::red);
QVERIFY(quickTileChangedSpy.wait());
QCOMPARE(quickTileChangedSpy.count(), 1);
QTEST(window->quickTileMode(), "expectedMode");
}
void QuickTilingTest::testX11QuickTiling_data()
@ -623,6 +577,7 @@ void QuickTilingTest::testX11QuickTiling()
QCOMPARE(window->quickTileMode(), mode);
QTEST(window->frameGeometry(), "expectedGeometry");
QCOMPARE(window->geometryRestore(), origGeo);
QEXPECT_FAIL("maximize", "For maximize we get two changed signals", Continue);
QCOMPARE(quickTileChangedSpy.count(), 1);
// quick tile to same edge again should also act like send to screen
@ -708,6 +663,7 @@ void QuickTilingTest::testX11QuickTilingAfterVertMaximize()
window->setQuickTileMode(mode, true);
QCOMPARE(window->quickTileMode(), mode);
QTEST(window->frameGeometry(), "expectedGeometry");
QEXPECT_FAIL("", "We get two changed events", Continue);
QCOMPARE(quickTileChangedSpy.count(), 1);
// and destroy the window again
@ -774,9 +730,6 @@ void QuickTilingTest::testShortcut()
const int numberOfQuickTileActions = shortcutList.count();
QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged);
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
for (QString shortcut : shortcutList) {
// invoke global shortcut through dbus
auto msg = QDBusMessage::createMethodCall(
@ -786,24 +739,28 @@ void QuickTilingTest::testShortcut()
QStringLiteral("invokeShortcut"));
msg.setArguments(QList<QVariant>{shortcut});
QDBusConnection::sessionBus().asyncCall(msg);
QVERIFY(surfaceConfigureRequestedSpy.wait());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).toSize(), Qt::red);
QVERIFY(quickTileChangedSpy.wait());
}
QCOMPARE(surfaceConfigureRequestedSpy.count(), numberOfQuickTileActions + 1);
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), expectedGeometry.size());
QCOMPARE(frameGeometryChangedSpy.count(), numberOfQuickTileActions);
QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged);
QTRY_COMPARE(quickTileChangedSpy.count(), numberOfQuickTileActions);
// geometry already changed
QCOMPARE(window->frameGeometry(), expectedGeometry);
// quick tile mode already changed
// at this point the geometry did not yet change
QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
// but quick tile mode already changed
QTEST(window->quickTileMode(), "expectedMode");
// but we got requested a new geometry
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), expectedGeometry.size());
// attach a new image
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), expectedGeometry.size(), Qt::red);
QVERIFY(frameGeometryChangedSpy.wait());
QEXPECT_FAIL("maximize", "Geometry changed called twice for maximize", Continue);
QCOMPARE(frameGeometryChangedSpy.count(), 1);
QCOMPARE(window->frameGeometry(), expectedGeometry);
}
@ -868,14 +825,16 @@ void QuickTilingTest::testScript()
QSignalSpy runningChangedSpy(s, &AbstractScript::runningChanged);
s->run();
QVERIFY(runningChangedSpy.wait());
QVERIFY(quickTileChangedSpy.wait());
QCOMPARE(quickTileChangedSpy.count(), 1);
QCOMPARE(runningChangedSpy.count(), 1);
QCOMPARE(runningChangedSpy.first().first().toBool(), true);
// at this point the geometry did not yet change
QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
// but requested quick tile mode already changed
QCOMPARE(window->requestedQuickTileMode(), expectedMode);
// but quick tile mode already changed
QCOMPARE(window->quickTileMode(), expectedMode);
// but we got requested a new geometry
QVERIFY(surfaceConfigureRequestedSpy.wait());
@ -887,8 +846,6 @@ void QuickTilingTest::testScript()
Test::render(surface.get(), expectedGeometry.size(), Qt::red);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(quickTileChangedSpy.count(), 1);
QCOMPARE(window->quickTileMode(), expectedMode);
QEXPECT_FAIL("maximize", "Geometry changed called twice for maximize", Continue);
QCOMPARE(frameGeometryChangedSpy.count(), 1);
QCOMPARE(window->frameGeometry(), expectedGeometry);

View File

@ -10,6 +10,7 @@
#include "core/output.h"
#include "generic_scene_opengl_test.h"
#include "opengl/glplatform.h"
#include "pipewiresourcestream.h"
#include "pointer_input.h"
#include "scene/workspacescene.h"
#include "wayland_server.h"
@ -19,7 +20,6 @@
#include <KWayland/Client/output.h>
#include <KWayland/Client/subsurface.h>
#include <KWayland/Client/surface.h>
#include <PipeWireSourceStream>
#include <QPainter>
#include <QScreen>
@ -110,9 +110,7 @@ std::optional<QImage> ScreencastingTest::oneFrameAndClose(Test::ScreencastingStr
std::optional<QImage> img;
connect(&pwStream, &PipeWireSourceStream::frameReceived, qGuiApp, [&img](const PipeWireFrame &frame) {
if (frame.dataFrame) {
img = frame.dataFrame->toImage();
}
img = frame.image;
});
QSignalSpy spy(&pwStream, &PipeWireSourceStream::frameReceived);

View File

@ -65,8 +65,6 @@ private Q_SLOTS:
void testPushBack();
void testObjectEdge_data();
void testObjectEdge();
void testMultipleEntry_data();
void testMultipleEntry();
void testKdeNetWmScreenEdgeShow();
};
@ -306,50 +304,6 @@ void ScreenEdgesTest::testObjectEdge()
QCOMPARE(spy.count(), 2);
}
void ScreenEdgesTest::testMultipleEntry_data()
{
QTest::addColumn<ElectricBorder>("border");
QTest::addColumn<QPointF>("triggerPoint");
QTest::addColumn<QPointF>("delta");
QTest::newRow("top") << ElectricTop << QPointF(640, 0) << QPointF(0, 50);
QTest::newRow("right") << ElectricRight << QPointF(1279, 512) << QPointF(-50, 0);
QTest::newRow("bottom") << ElectricBottom << QPointF(640, 1023) << QPointF(0, -50);
QTest::newRow("left") << ElectricLeft << QPointF(0, 512) << QPointF(50, 0);
}
void ScreenEdgesTest::testMultipleEntry()
{
TestObject callback;
QSignalSpy spy(&callback, &TestObject::gotCallback);
// Reserve a screen edge border.
QFETCH(ElectricBorder, border);
workspace()->screenEdges()->reserve(border, &callback, "callback");
QFETCH(QPointF, triggerPoint);
QFETCH(QPointF, delta);
qint64 timestamp = 0;
while (timestamp < 300) {
// doesn't activate from repeated entries of short duration
Test::pointerMotion(triggerPoint, timestamp);
QVERIFY(spy.isEmpty());
timestamp += 50;
Test::pointerMotion(triggerPoint + delta, timestamp);
QVERIFY(spy.isEmpty());
timestamp += 50;
}
// and this one triggers
Test::pointerMotion(triggerPoint, timestamp);
timestamp += 110;
Test::pointerMotion(triggerPoint, timestamp);
QVERIFY(!spy.isEmpty());
QCOMPARE(spy.count(), 1);
}
static void enableAutoHide(xcb_connection_t *connection, xcb_window_t windowId, ElectricBorder border)
{
if (border == ElectricNone) {
@ -460,14 +414,15 @@ void ScreenEdgesTest::testKdeNetWmScreenEdgeShow()
ScreenEdgePropertyMonitor screenEdgeMonitor(c.get(), windowId);
QSignalSpy withdrawnSpy(&screenEdgeMonitor, &ScreenEdgePropertyMonitor::withdrawn);
QSignalSpy hiddenChangedSpy(window, &Window::hiddenChanged);
QSignalSpy windowShownSpy(window, &Window::windowShown);
QSignalSpy windowHiddenSpy(window, &Window::windowHidden);
quint32 timestamp = 0;
// The window will be shown when the pointer approaches its reserved screen edge.
{
enableAutoHide(c.get(), windowId, ElectricBottom);
xcb_flush(c.get());
QVERIFY(hiddenChangedSpy.wait());
QVERIFY(windowHiddenSpy.wait());
QVERIFY(!window->isShown());
Test::pointerMotion(QPointF(640, 1023), timestamp);
@ -484,7 +439,7 @@ void ScreenEdgesTest::testKdeNetWmScreenEdgeShow()
{
enableAutoHide(c.get(), windowId, ElectricBottom);
xcb_flush(c.get());
QVERIFY(hiddenChangedSpy.wait());
QVERIFY(windowHiddenSpy.wait());
QVERIFY(!window->isShown());
Test::touchDown(0, QPointF(640, 1023), timestamp++);
@ -498,7 +453,7 @@ void ScreenEdgesTest::testKdeNetWmScreenEdgeShow()
{
enableAutoHide(c.get(), windowId, ElectricBottom);
xcb_flush(c.get());
QVERIFY(hiddenChangedSpy.wait());
QVERIFY(windowHiddenSpy.wait());
QVERIFY(!window->isShown());
workspace()->screenEdges()->recreateEdges();
@ -507,7 +462,7 @@ void ScreenEdgesTest::testKdeNetWmScreenEdgeShow()
enableAutoHide(c.get(), windowId, ElectricNone);
xcb_flush(c.get());
QVERIFY(hiddenChangedSpy.wait());
QVERIFY(windowShownSpy.wait());
QVERIFY(window->isShown());
}
@ -515,12 +470,12 @@ void ScreenEdgesTest::testKdeNetWmScreenEdgeShow()
{
enableAutoHide(c.get(), windowId, ElectricBottom);
xcb_flush(c.get());
QVERIFY(hiddenChangedSpy.wait());
QVERIFY(windowHiddenSpy.wait());
QVERIFY(!window->isShown());
enableAutoHide(c.get(), windowId, ElectricNone);
xcb_flush(c.get());
QVERIFY(hiddenChangedSpy.wait());
QVERIFY(windowShownSpy.wait());
QVERIFY(window->isShown());
}
@ -529,7 +484,7 @@ void ScreenEdgesTest::testKdeNetWmScreenEdgeShow()
QSignalSpy approachingSpy(workspace()->screenEdges(), &ScreenEdges::approaching);
enableAutoHide(c.get(), windowId, ElectricBottom);
xcb_flush(c.get());
QVERIFY(hiddenChangedSpy.wait());
QVERIFY(windowHiddenSpy.wait());
QVERIFY(!window->isShown());
Test::pointerMotion(QPointF(640, 1020), timestamp++);
@ -539,7 +494,7 @@ void ScreenEdgesTest::testKdeNetWmScreenEdgeShow()
enableAutoHide(c.get(), windowId, ElectricNone);
xcb_flush(c.get());
QVERIFY(hiddenChangedSpy.wait());
QVERIFY(windowShownSpy.wait());
QVERIFY(window->isShown());
QVERIFY(approachingSpy.last().at(1).toReal() == 0.0);

View File

@ -30,8 +30,10 @@ private Q_SLOTS:
void initTestCase();
void init();
void cleanup();
void testActiveOutputFollowsMouse_data();
void testActiveOutputFollowsMouse();
void testCurrent_data();
void testCurrent();
void testCurrentWithFollowsMouse_data();
void testCurrentWithFollowsMouse();
void testCurrentPoint_data();
void testCurrentPoint();
};
@ -90,7 +92,30 @@ void ScreensTest::cleanup()
});
}
void ScreensTest::testActiveOutputFollowsMouse_data()
void ScreensTest::testCurrent_data()
{
QTest::addColumn<int>("currentId");
QTest::newRow("first") << 0;
QTest::newRow("second") << 1;
}
void ScreensTest::testCurrent()
{
QFETCH(int, currentId);
Output *output = workspace()->outputs().at(currentId);
// Disable "active screen follows mouse"
auto group = kwinApp()->config()->group(QStringLiteral("Windows"));
group.writeEntry("ActiveMouseScreen", false);
group.sync();
workspace()->slotReconfigure();
workspace()->setActiveOutput(output);
QCOMPARE(workspace()->activeOutput(), output);
}
void ScreensTest::testCurrentWithFollowsMouse_data()
{
QTest::addColumn<QList<QRect>>("geometries");
QTest::addColumn<QPoint>("cursorPos");
@ -102,12 +127,12 @@ void ScreensTest::testActiveOutputFollowsMouse_data()
QTest::newRow("gap") << QList<QRect>{{QRect{0, 0, 10, 20}, QRect{20, 40, 10, 20}}} << QPoint(15, 30) << 1;
}
void ScreensTest::testActiveOutputFollowsMouse()
void ScreensTest::testCurrentWithFollowsMouse()
{
auto edgeBarrierGroup = kwinApp()->config()->group(QStringLiteral("EdgeBarrier"));
edgeBarrierGroup.writeEntry("EdgeBarrier", 0);
edgeBarrierGroup.writeEntry("CornerBarrier", false);
edgeBarrierGroup.sync();
// Enable "active screen follows mouse"
auto group = kwinApp()->config()->group(QStringLiteral("Windows"));
group.writeEntry("ActiveMouseScreen", true);
group.sync();
workspace()->slotReconfigure();
QFETCH(QList<QRect>, geometries);

View File

@ -84,7 +84,7 @@ void StackingOrderTest::testTransientIsAboveParent()
// Create the parent.
std::unique_ptr<KWayland::Client::Surface> parentSurface = Test::createSurface();
QVERIFY(parentSurface);
std::unique_ptr<Test::XdgToplevel> parentShellSurface(Test::createXdgToplevelSurface(parentSurface.get()));
std::unique_ptr<Test::XdgToplevel> parentShellSurface(Test::createXdgToplevelSurface(parentSurface.get(), parentSurface.get()));
QVERIFY(parentShellSurface);
Window *parent = Test::renderAndWaitForShown(parentSurface.get(), QSize(256, 256), Qt::blue);
QVERIFY(parent);
@ -97,7 +97,7 @@ void StackingOrderTest::testTransientIsAboveParent()
// Create the transient.
std::unique_ptr<KWayland::Client::Surface> transientSurface = Test::createSurface();
QVERIFY(transientSurface);
std::unique_ptr<Test::XdgToplevel> transientShellSurface(Test::createXdgToplevelSurface(transientSurface.get()));
std::unique_ptr<Test::XdgToplevel> transientShellSurface(Test::createXdgToplevelSurface(transientSurface.get(), transientSurface.get()));
QVERIFY(transientShellSurface);
transientShellSurface->set_parent(parentShellSurface->object());
Window *transient = Test::renderAndWaitForShown(transientSurface.get(), QSize(128, 128), Qt::red);
@ -123,7 +123,7 @@ void StackingOrderTest::testRaiseTransient()
// Create the parent.
std::unique_ptr<KWayland::Client::Surface> parentSurface = Test::createSurface();
QVERIFY(parentSurface);
std::unique_ptr<Test::XdgToplevel> parentShellSurface(Test::createXdgToplevelSurface(parentSurface.get()));
std::unique_ptr<Test::XdgToplevel> parentShellSurface(Test::createXdgToplevelSurface(parentSurface.get(), parentSurface.get()));
QVERIFY(parentShellSurface);
Window *parent = Test::renderAndWaitForShown(parentSurface.get(), QSize(256, 256), Qt::blue);
QVERIFY(parent);
@ -136,7 +136,7 @@ void StackingOrderTest::testRaiseTransient()
// Create the transient.
std::unique_ptr<KWayland::Client::Surface> transientSurface = Test::createSurface();
QVERIFY(transientSurface);
std::unique_ptr<Test::XdgToplevel> transientShellSurface(Test::createXdgToplevelSurface(transientSurface.get()));
std::unique_ptr<Test::XdgToplevel> transientShellSurface(Test::createXdgToplevelSurface(transientSurface.get(), transientSurface.get()));
QVERIFY(transientShellSurface);
transientShellSurface->set_parent(parentShellSurface->object());
Window *transient = Test::renderAndWaitForShown(transientSurface.get(), QSize(128, 128), Qt::red);
@ -150,7 +150,7 @@ void StackingOrderTest::testRaiseTransient()
// Create a window that doesn't have any relationship to the parent or the transient.
std::unique_ptr<KWayland::Client::Surface> anotherSurface = Test::createSurface();
QVERIFY(anotherSurface);
std::unique_ptr<Test::XdgToplevel> anotherShellSurface(Test::createXdgToplevelSurface(anotherSurface.get()));
std::unique_ptr<Test::XdgToplevel> anotherShellSurface(Test::createXdgToplevelSurface(anotherSurface.get(), anotherSurface.get()));
QVERIFY(anotherShellSurface);
Window *anotherWindow = Test::renderAndWaitForShown(anotherSurface.get(), QSize(128, 128), Qt::green);
QVERIFY(anotherWindow);
@ -200,7 +200,8 @@ void StackingOrderTest::testDeletedTransient()
// Create the parent.
std::unique_ptr<KWayland::Client::Surface> parentSurface = Test::createSurface();
QVERIFY(parentSurface);
std::unique_ptr<Test::XdgToplevel> parentShellSurface(Test::createXdgToplevelSurface(parentSurface.get()));
std::unique_ptr<Test::XdgToplevel>
parentShellSurface(Test::createXdgToplevelSurface(parentSurface.get(), parentSurface.get()));
QVERIFY(parentShellSurface);
Window *parent = Test::renderAndWaitForShown(parentSurface.get(), QSize(256, 256), Qt::blue);
QVERIFY(parent);
@ -212,7 +213,7 @@ void StackingOrderTest::testDeletedTransient()
// Create the first transient.
std::unique_ptr<KWayland::Client::Surface> transient1Surface = Test::createSurface();
QVERIFY(transient1Surface);
std::unique_ptr<Test::XdgToplevel> transient1ShellSurface(Test::createXdgToplevelSurface(transient1Surface.get()));
std::unique_ptr<Test::XdgToplevel> transient1ShellSurface(Test::createXdgToplevelSurface(transient1Surface.get(), transient1Surface.get()));
QVERIFY(transient1ShellSurface);
transient1ShellSurface->set_parent(parentShellSurface->object());
Window *transient1 = Test::renderAndWaitForShown(transient1Surface.get(), QSize(128, 128), Qt::red);
@ -226,7 +227,7 @@ void StackingOrderTest::testDeletedTransient()
// Create the second transient.
std::unique_ptr<KWayland::Client::Surface> transient2Surface = Test::createSurface();
QVERIFY(transient2Surface);
std::unique_ptr<Test::XdgToplevel> transient2ShellSurface(Test::createXdgToplevelSurface(transient2Surface.get()));
std::unique_ptr<Test::XdgToplevel> transient2ShellSurface(Test::createXdgToplevelSurface(transient2Surface.get(), transient2Surface.get()));
QVERIFY(transient2ShellSurface);
transient2ShellSurface->set_parent(transient1ShellSurface->object());
Window *transient2 = Test::renderAndWaitForShown(transient2Surface.get(), QSize(128, 128), Qt::red);
@ -516,7 +517,7 @@ void StackingOrderTest::testRaiseGroupTransient()
// Create a Wayland window that is not a member of the window group.
std::unique_ptr<KWayland::Client::Surface> anotherSurface = Test::createSurface();
QVERIFY(anotherSurface);
std::unique_ptr<Test::XdgToplevel> anotherShellSurface(Test::createXdgToplevelSurface(anotherSurface.get()));
std::unique_ptr<Test::XdgToplevel> anotherShellSurface(Test::createXdgToplevelSurface(anotherSurface.get(), anotherSurface.get()));
QVERIFY(anotherShellSurface);
Window *anotherWindow = Test::renderAndWaitForShown(anotherSurface.get(), QSize(128, 128), Qt::green);
QVERIFY(anotherWindow);
@ -758,7 +759,7 @@ void StackingOrderTest::testKeepAbove()
// Create the first window.
std::unique_ptr<KWayland::Client::Surface> surface1 = Test::createSurface();
QVERIFY(surface1);
std::unique_ptr<Test::XdgToplevel> shellSurface1(Test::createXdgToplevelSurface(surface1.get()));
std::unique_ptr<Test::XdgToplevel> shellSurface1(Test::createXdgToplevelSurface(surface1.get(), surface1.get()));
QVERIFY(shellSurface1);
Window *window1 = Test::renderAndWaitForShown(surface1.get(), QSize(128, 128), Qt::green);
QVERIFY(window1);
@ -770,7 +771,7 @@ void StackingOrderTest::testKeepAbove()
// Create the second window.
std::unique_ptr<KWayland::Client::Surface> surface2 = Test::createSurface();
QVERIFY(surface2);
std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get()));
std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get(), surface2.get()));
QVERIFY(shellSurface2);
Window *window2 = Test::renderAndWaitForShown(surface2.get(), QSize(128, 128), Qt::green);
QVERIFY(window2);
@ -802,7 +803,7 @@ void StackingOrderTest::testKeepBelow()
// Create the first window.
std::unique_ptr<KWayland::Client::Surface> surface1 = Test::createSurface();
QVERIFY(surface1);
std::unique_ptr<Test::XdgToplevel> shellSurface1(Test::createXdgToplevelSurface(surface1.get()));
std::unique_ptr<Test::XdgToplevel> shellSurface1(Test::createXdgToplevelSurface(surface1.get(), surface1.get()));
QVERIFY(shellSurface1);
Window *window1 = Test::renderAndWaitForShown(surface1.get(), QSize(128, 128), Qt::green);
QVERIFY(window1);
@ -814,7 +815,7 @@ void StackingOrderTest::testKeepBelow()
// Create the second window.
std::unique_ptr<KWayland::Client::Surface> surface2 = Test::createSurface();
QVERIFY(surface2);
std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get()));
std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get(), surface2.get()));
QVERIFY(shellSurface2);
Window *window2 = Test::renderAndWaitForShown(surface2.get(), QSize(128, 128), Qt::green);
QVERIFY(window2);
@ -849,7 +850,7 @@ void StackingOrderTest::testPreserveRelativeWindowStacking()
for (int i = 0; i < windowsQuantity; i++) {
surfaces[i] = Test::createSurface();
QVERIFY(surfaces[i]);
shellSurfaces[i] = Test::createXdgToplevelSurface(surfaces[i].get());
shellSurfaces[i] = std::unique_ptr<Test::XdgToplevel>(Test::createXdgToplevelSurface(surfaces[i].get(), surfaces[i].get()));
QVERIFY(shellSurfaces[i]);
}

View File

@ -367,7 +367,7 @@ void StrutsTest::testX11Struts()
QVERIFY(window);
QCOMPARE(window->window(), windowId);
QVERIFY(!window->isDecorated());
QCOMPARE(window->windowType(), WindowType::Dock);
QCOMPARE(window->windowType(), NET::Dock);
QCOMPARE(window->frameGeometry(), windowGeometry);
// this should have affected the client area
@ -479,7 +479,7 @@ void StrutsTest::test363804()
QVERIFY(window);
QCOMPARE(window->window(), windowId);
QVERIFY(!window->isDecorated());
QCOMPARE(window->windowType(), WindowType::Dock);
QCOMPARE(window->windowType(), NET::Dock);
QCOMPARE(window->frameGeometry(), windowGeometry);
// now verify the actual updated client areas
@ -557,7 +557,7 @@ void StrutsTest::testLeftScreenSmallerBottomAligned()
QVERIFY(window);
QCOMPARE(window->window(), windowId);
QVERIFY(!window->isDecorated());
QCOMPARE(window->windowType(), WindowType::Dock);
QCOMPARE(window->windowType(), NET::Dock);
QCOMPARE(window->frameGeometry(), windowGeometry);
// now verify the actual updated client areas
@ -638,7 +638,7 @@ void StrutsTest::testWindowMoveWithPanelBetweenScreens()
QVERIFY(window);
QCOMPARE(window->window(), windowId);
QVERIFY(!window->isDecorated());
QCOMPARE(window->windowType(), WindowType::Dock);
QCOMPARE(window->windowType(), NET::Dock);
QCOMPARE(window->frameGeometry(), windowGeometry);
// now verify the actual updated client areas

View File

@ -269,21 +269,6 @@ SecurityContextManagerV1::~SecurityContextManagerV1()
destroy();
}
XdgWmDialogV1::~XdgWmDialogV1()
{
destroy();
}
XdgDialogV1::XdgDialogV1(XdgWmDialogV1 *wm, XdgToplevel *toplevel)
: QtWayland::xdg_dialog_v1(wm->get_xdg_dialog(toplevel->object()))
{
}
XdgDialogV1::~XdgDialogV1()
{
destroy();
}
static struct
{
KWayland::Client::ConnectionThread *connection = nullptr;
@ -317,7 +302,6 @@ static struct
CursorShapeManagerV1 *cursorShapeManagerV1 = nullptr;
FakeInput *fakeInput = nullptr;
SecurityContextManagerV1 *securityContextManagerV1 = nullptr;
XdgWmDialogV1 *xdgWmDialogV1;
} s_waylandConnection;
MockInputMethod *inputMethod()
@ -373,7 +357,8 @@ void MockInputMethod::zwp_input_method_v1_deactivate(struct ::zwp_input_method_c
m_inputSurface->release();
m_inputSurface->destroy();
m_inputSurface.reset();
m_inputMethodSurface.reset();
delete m_inputMethodSurface;
m_inputMethodSurface = nullptr;
}
}
@ -533,12 +518,6 @@ bool setupWaylandConnection(AdditionalWaylandInterfaces flags)
s_waylandConnection.securityContextManagerV1->init(*registry, name, version);
}
}
if (flags & AdditionalWaylandInterface::XdgDialogV1) {
if (interface == xdg_wm_dialog_v1_interface.name) {
s_waylandConnection.xdgWmDialogV1 = new XdgWmDialogV1();
s_waylandConnection.xdgWmDialogV1->init(*registry, name, version);
}
}
});
QSignalSpy allAnnounced(registry, &KWayland::Client::Registry::interfacesAnnounced);
@ -664,8 +643,6 @@ void destroyWaylandConnection()
s_waylandConnection.fakeInput = nullptr;
delete s_waylandConnection.securityContextManagerV1;
s_waylandConnection.securityContextManagerV1 = nullptr;
delete s_waylandConnection.xdgWmDialogV1;
s_waylandConnection.xdgWmDialogV1 = nullptr;
delete s_waylandConnection.queue; // Must be destroyed last
s_waylandConnection.queue = nullptr;
@ -928,19 +905,20 @@ std::unique_ptr<KWayland::Client::Surface> createSurface()
return s->isValid() ? std::move(s) : nullptr;
}
std::unique_ptr<KWayland::Client::SubSurface> createSubSurface(KWayland::Client::Surface *surface, KWayland::Client::Surface *parentSurface)
KWayland::Client::SubSurface *createSubSurface(KWayland::Client::Surface *surface, KWayland::Client::Surface *parentSurface, QObject *parent)
{
if (!s_waylandConnection.subCompositor) {
return nullptr;
}
std::unique_ptr<KWayland::Client::SubSurface> s(s_waylandConnection.subCompositor->createSubSurface(surface, parentSurface));
auto s = s_waylandConnection.subCompositor->createSubSurface(surface, parentSurface, parent);
if (!s->isValid()) {
delete s;
return nullptr;
}
return s;
}
std::unique_ptr<LayerSurfaceV1> createLayerSurfaceV1(KWayland::Client::Surface *surface, const QString &scope, KWayland::Client::Output *output, LayerShellV1::layer layer)
LayerSurfaceV1 *createLayerSurfaceV1(KWayland::Client::Surface *surface, const QString &scope, KWayland::Client::Output *output, LayerShellV1::layer layer)
{
LayerShellV1 *shell = s_waylandConnection.layerShellV1;
if (!shell) {
@ -953,20 +931,22 @@ std::unique_ptr<LayerSurfaceV1> createLayerSurfaceV1(KWayland::Client::Surface *
nativeOutput = *output;
}
auto shellSurface = std::make_unique<LayerSurfaceV1>();
LayerSurfaceV1 *shellSurface = new LayerSurfaceV1();
shellSurface->init(shell->get_layer_surface(*surface, nativeOutput, layer, scope));
return shellSurface;
}
std::unique_ptr<QtWayland::zwp_input_panel_surface_v1> createInputPanelSurfaceV1(KWayland::Client::Surface *surface, KWayland::Client::Output *output, MockInputMethod::Mode mode)
QtWayland::zwp_input_panel_surface_v1 *createInputPanelSurfaceV1(KWayland::Client::Surface *surface, KWayland::Client::Output *output, MockInputMethod::Mode mode)
{
if (!s_waylandConnection.inputPanelV1) {
qWarning() << "Unable to create the input panel surface. The interface input_panel global is not bound";
return nullptr;
}
auto s = std::make_unique<QtWayland::zwp_input_panel_surface_v1>(s_waylandConnection.inputPanelV1->get_input_panel_surface(*surface));
QtWayland::zwp_input_panel_surface_v1 *s = new QtWayland::zwp_input_panel_surface_v1(s_waylandConnection.inputPanelV1->get_input_panel_surface(*surface));
if (!s->isInitialized()) {
delete s;
return nullptr;
}
@ -982,13 +962,13 @@ std::unique_ptr<QtWayland::zwp_input_panel_surface_v1> createInputPanelSurfaceV1
return s;
}
std::unique_ptr<FractionalScaleV1> createFractionalScaleV1(KWayland::Client::Surface *surface)
FractionalScaleV1 *createFractionalScaleV1(KWayland::Client::Surface *surface)
{
if (!s_waylandConnection.fractionalScaleManagerV1) {
qWarning() << "Unable to create fractional scale surface. The global is not bound";
return nullptr;
}
auto scale = std::make_unique<FractionalScaleV1>();
auto scale = new FractionalScaleV1();
scale->init(s_waylandConnection.fractionalScaleManagerV1->get_fractional_scale(*surface));
return scale;
@ -1004,12 +984,12 @@ static void waitForConfigured(XdgSurface *shellSurface)
shellSurface->ack_configure(surfaceConfigureRequestedSpy.last().first().toUInt());
}
std::unique_ptr<XdgToplevel> createXdgToplevelSurface(KWayland::Client::Surface *surface)
XdgToplevel *createXdgToplevelSurface(KWayland::Client::Surface *surface, QObject *parent)
{
return createXdgToplevelSurface(surface, CreationSetup::CreateAndConfigure);
return createXdgToplevelSurface(surface, CreationSetup::CreateAndConfigure, parent);
}
std::unique_ptr<XdgToplevel> createXdgToplevelSurface(KWayland::Client::Surface *surface, CreationSetup configureMode)
XdgToplevel *createXdgToplevelSurface(KWayland::Client::Surface *surface, CreationSetup configureMode, QObject *parent)
{
XdgShell *shell = s_waylandConnection.xdgShell;
@ -1019,7 +999,7 @@ std::unique_ptr<XdgToplevel> createXdgToplevelSurface(KWayland::Client::Surface
}
XdgSurface *xdgSurface = new XdgSurface(shell, surface);
std::unique_ptr<XdgToplevel> xdgToplevel = std::make_unique<XdgToplevel>(xdgSurface);
XdgToplevel *xdgToplevel = new XdgToplevel(xdgSurface, parent);
if (configureMode == CreationSetup::CreateAndConfigure) {
waitForConfigured(xdgSurface);
@ -1028,25 +1008,7 @@ std::unique_ptr<XdgToplevel> createXdgToplevelSurface(KWayland::Client::Surface
return xdgToplevel;
}
std::unique_ptr<XdgToplevel> createXdgToplevelSurface(KWayland::Client::Surface *surface, std::function<void(XdgToplevel *toplevel)> setup)
{
XdgShell *shell = s_waylandConnection.xdgShell;
if (!shell) {
qWarning() << "Could not create an xdg_toplevel surface because xdg_wm_base global is not bound";
return nullptr;
}
XdgSurface *xdgSurface = new XdgSurface(shell, surface);
std::unique_ptr<XdgToplevel> xdgToplevel = std::make_unique<XdgToplevel>(xdgSurface);
setup(xdgToplevel.get());
waitForConfigured(xdgSurface);
return xdgToplevel;
}
std::unique_ptr<XdgPositioner> createXdgPositioner()
XdgPositioner *createXdgPositioner()
{
XdgShell *shell = s_waylandConnection.xdgShell;
@ -1055,10 +1017,11 @@ std::unique_ptr<XdgPositioner> createXdgPositioner()
return nullptr;
}
return std::make_unique<XdgPositioner>(shell);
return new XdgPositioner(shell);
}
std::unique_ptr<XdgPopup> createXdgPopupSurface(KWayland::Client::Surface *surface, XdgSurface *parentSurface, XdgPositioner *positioner, CreationSetup configureMode)
XdgPopup *createXdgPopupSurface(KWayland::Client::Surface *surface, XdgSurface *parentSurface, XdgPositioner *positioner,
CreationSetup configureMode, QObject *parent)
{
XdgShell *shell = s_waylandConnection.xdgShell;
@ -1068,7 +1031,7 @@ std::unique_ptr<XdgPopup> createXdgPopupSurface(KWayland::Client::Surface *surfa
}
XdgSurface *xdgSurface = new XdgSurface(shell, surface);
std::unique_ptr<XdgPopup> xdgPopup = std::make_unique<XdgPopup>(xdgSurface, parentSurface, positioner);
XdgPopup *xdgPopup = new XdgPopup(xdgSurface, parentSurface, positioner, parent);
if (configureMode == CreationSetup::CreateAndConfigure) {
waitForConfigured(xdgSurface);
@ -1077,7 +1040,7 @@ std::unique_ptr<XdgPopup> createXdgPopupSurface(KWayland::Client::Surface *surfa
return xdgPopup;
}
std::unique_ptr<XdgToplevelDecorationV1> createXdgToplevelDecorationV1(XdgToplevel *toplevel)
XdgToplevelDecorationV1 *createXdgToplevelDecorationV1(XdgToplevel *toplevel, QObject *parent)
{
XdgDecorationManagerV1 *manager = s_waylandConnection.xdgDecorationManagerV1;
@ -1086,10 +1049,10 @@ std::unique_ptr<XdgToplevelDecorationV1> createXdgToplevelDecorationV1(XdgToplev
return nullptr;
}
return std::make_unique<XdgToplevelDecorationV1>(manager, toplevel);
return new XdgToplevelDecorationV1(manager, toplevel, parent);
}
std::unique_ptr<IdleInhibitorV1> createIdleInhibitorV1(KWayland::Client::Surface *surface)
IdleInhibitorV1 *createIdleInhibitorV1(KWayland::Client::Surface *surface)
{
IdleInhibitManagerV1 *manager = s_waylandConnection.idleInhibitManagerV1;
if (!manager) {
@ -1097,10 +1060,10 @@ std::unique_ptr<IdleInhibitorV1> createIdleInhibitorV1(KWayland::Client::Surface
return nullptr;
}
return std::make_unique<IdleInhibitorV1>(manager, surface);
return new IdleInhibitorV1(manager, surface);
}
std::unique_ptr<AutoHideScreenEdgeV1> createAutoHideScreenEdgeV1(KWayland::Client::Surface *surface, uint32_t border)
AutoHideScreenEdgeV1 *createAutoHideScreenEdgeV1(KWayland::Client::Surface *surface, uint32_t border)
{
ScreenEdgeManagerV1 *manager = s_waylandConnection.screenEdgeManagerV1;
if (!manager) {
@ -1108,10 +1071,10 @@ std::unique_ptr<AutoHideScreenEdgeV1> createAutoHideScreenEdgeV1(KWayland::Clien
return nullptr;
}
return std::make_unique<AutoHideScreenEdgeV1>(manager, surface, border);
return new AutoHideScreenEdgeV1(manager, surface, border);
}
std::unique_ptr<CursorShapeDeviceV1> createCursorShapeDeviceV1(KWayland::Client::Pointer *pointer)
CursorShapeDeviceV1 *createCursorShapeDeviceV1(KWayland::Client::Pointer *pointer)
{
CursorShapeManagerV1 *manager = s_waylandConnection.cursorShapeManagerV1;
if (!manager) {
@ -1119,17 +1082,7 @@ std::unique_ptr<CursorShapeDeviceV1> createCursorShapeDeviceV1(KWayland::Client:
return nullptr;
}
return std::make_unique<CursorShapeDeviceV1>(manager, pointer);
}
std::unique_ptr<XdgDialogV1> createXdgDialogV1(XdgToplevel *toplevel)
{
XdgWmDialogV1 *wm = s_waylandConnection.xdgWmDialogV1;
if (!wm) {
qWarning() << "Could not create a xdg_dialog_v1 because xdg_wm_dialog_v1 global is not bound";
return nullptr;
}
return std::make_unique<XdgDialogV1>(wm, toplevel);
return new CursorShapeDeviceV1(manager, pointer);
}
bool waitForWindowClosed(Window *window)
@ -1230,7 +1183,6 @@ bool renderNodeAvailable()
});
}
#if KWIN_BUILD_X11
void XcbConnectionDeleter::operator()(xcb_connection_t *pointer)
{
xcb_disconnect(pointer);
@ -1248,7 +1200,6 @@ Test::XcbConnectionPtr createX11Connection()
e.exec();
return Test::XcbConnectionPtr(future.result());
}
#endif
WaylandOutputManagementV2::WaylandOutputManagementV2(struct ::wl_registry *registry, int id, int version)
: QObject()
@ -1611,6 +1562,11 @@ bool VirtualInputDevice::isKeyboard() const
return m_keyboard;
}
bool VirtualInputDevice::isAlphaNumericKeyboard() const
{
return m_keyboard;
}
bool VirtualInputDevice::isPointer() const
{
return m_pointer;

View File

@ -46,14 +46,7 @@ private Q_SLOTS:
void testGestureDetection();
private:
struct WindowHandle
{
Window *window;
std::unique_ptr<KWayland::Client::Surface> surface;
std::unique_ptr<Test::XdgToplevel> shellSurface;
std::unique_ptr<Test::XdgToplevelDecorationV1> decoration;
};
WindowHandle showWindow(bool decorated = false);
std::pair<Window *, std::unique_ptr<KWayland::Client::Surface>> showWindow(bool decorated = false);
KWayland::Client::Touch *m_touch = nullptr;
};
@ -94,22 +87,21 @@ void TouchInputTest::cleanup()
Test::destroyWaylandConnection();
}
TouchInputTest::WindowHandle TouchInputTest::showWindow(bool decorated)
std::pair<Window *, std::unique_ptr<KWayland::Client::Surface>> TouchInputTest::showWindow(bool decorated)
{
#define VERIFY(statement) \
if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__)) \
return {};
return {nullptr, nullptr};
#define COMPARE(actual, expected) \
if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__)) \
return {};
return {nullptr, nullptr};
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
VERIFY(surface.get());
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly);
VERIFY(shellSurface.get());
std::unique_ptr<Test::XdgToplevelDecorationV1> decoration;
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly, surface.get());
VERIFY(shellSurface);
if (decorated) {
decoration = Test::createXdgToplevelDecorationV1(shellSurface.get());
auto decoration = Test::createXdgToplevelDecorationV1(shellSurface, shellSurface);
decoration->set_mode(Test::XdgToplevelDecorationV1::mode_server_side);
}
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
@ -125,7 +117,7 @@ TouchInputTest::WindowHandle TouchInputTest::showWindow(bool decorated)
#undef VERIFY
#undef COMPARE
return {window, std::move(surface), std::move(shellSurface), std::move(decoration)};
return {window, std::move(surface)};
}
void TouchInputTest::testTouchHidesCursor()
@ -163,7 +155,7 @@ void TouchInputTest::testMultipleTouchPoints_data()
void TouchInputTest::testMultipleTouchPoints()
{
QFETCH(bool, decorated);
auto [window, surface, shellSurface, decoration] = showWindow(decorated);
auto [window, surface] = showWindow(decorated);
QCOMPARE(window->isDecorated(), decorated);
window->move(QPoint(100, 100));
QVERIFY(window);
@ -218,7 +210,7 @@ void TouchInputTest::testMultipleTouchPoints()
void TouchInputTest::testCancel()
{
auto [window, surface, shellSurface, decoration] = showWindow();
auto [window, surface] = showWindow();
window->move(QPoint(100, 100));
QVERIFY(window);
QSignalSpy sequenceStartedSpy(m_touch, &KWayland::Client::Touch::sequenceStarted);
@ -241,9 +233,9 @@ void TouchInputTest::testTouchMouseAction()
// this test verifies that a touch down on an inactive window will activate it
// create two windows
auto [c1, surface, shellSurface, decoration] = showWindow();
auto [c1, surface] = showWindow();
QVERIFY(c1);
auto [c2, surface2, shellSurface2, decoration2] = showWindow();
auto [c2, surface2] = showWindow();
QVERIFY(c2);
QVERIFY(!c1->isActive());

View File

@ -114,7 +114,7 @@ void TransientPlacementTest::testXdgPopup_data()
.anchor = Test::XdgPositioner::anchor_none,
.gravity = Test::XdgPositioner::gravity_bottom_right,
};
QTest::newRow("anchorCenter") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorCenter << QRect(550, 550, 200, 200);
QTest::newRow("anchorCentre") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorCenter << QRect(550, 550, 200, 200);
const PopupLayout layoutAnchorTopLeft{
.anchorRect = QRect(50, 50, 400, 400),
@ -189,7 +189,7 @@ void TransientPlacementTest::testXdgPopup_data()
.anchor = Test::XdgPositioner::anchor_bottom_right,
.gravity = Test::XdgPositioner::gravity_none,
};
QTest::newRow("gravityCenter") << QSize(500, 500) << QPoint(300, 300) << layoutGravityCenter << QRect(650, 650, 200, 200);
QTest::newRow("gravityCentre") << QSize(500, 500) << QPoint(300, 300) << layoutGravityCenter << QRect(650, 650, 200, 200);
const PopupLayout layoutGravityTopLeft{
.anchorRect = QRect(50, 50, 400, 400),
@ -423,7 +423,7 @@ void TransientPlacementTest::testXdgPopup()
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
QVERIFY(surface);
std::unique_ptr<Test::XdgToplevel> parentShellSurface = Test::createXdgToplevelSurface(surface.get());
auto parentShellSurface = Test::createXdgToplevelSurface(surface.get(), Test::waylandCompositor());
QVERIFY(parentShellSurface);
auto parent = Test::renderAndWaitForShown(surface.get(), parentSize, Qt::blue);
QVERIFY(parent);
@ -480,7 +480,7 @@ void TransientPlacementTest::testXdgPopupWithPanel()
QVERIFY(dockConfigureRequestedSpy.wait());
auto dock = Test::renderAndWaitForShown(dockSurface.get(), dockConfigureRequestedSpy.last().at(1).toSize(), Qt::blue);
QVERIFY(dock);
QCOMPARE(dock->windowType(), WindowType::Dock);
QCOMPARE(dock->windowType(), NET::Dock);
QVERIFY(dock->isDock());
QCOMPARE(dock->frameGeometry(), QRect(0, output->geometry().height() - 50, 1280, 50));
QCOMPARE(dock->hasStrut(), true);
@ -497,7 +497,7 @@ void TransientPlacementTest::testXdgPopupWithPanel()
QVERIFY(!parent->isDecorated());
parent->move(QPointF(0, output->geometry().height() - 600));
parent->moveResize(parent->keepInArea(parent->moveResizeGeometry(), workspace()->clientArea(PlacementArea, parent)));
parent->keepInArea(workspace()->clientArea(PlacementArea, parent));
QCOMPARE(parent->frameGeometry(), QRect(0, output->geometry().height() - 600 - 50, 800, 600));
std::unique_ptr<KWayland::Client::Surface> transientSurface(Test::createSurface());
@ -522,7 +522,7 @@ void TransientPlacementTest::testXdgPopupWithPanel()
QVERIFY(Test::waitForWindowClosed(transient));
// now parent to fullscreen - on fullscreen the panel is ignored
QSignalSpy toplevelConfigureRequestedSpy(parentShellSurface.get(), &Test::XdgToplevel::configureRequested);
QSignalSpy toplevelConfigureRequestedSpy(parentShellSurface, &Test::XdgToplevel::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(parentShellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
parent->setFullScreen(true);
QVERIFY(surfaceConfigureRequestedSpy.wait());
@ -542,7 +542,7 @@ void TransientPlacementTest::testXdgPopupWithPanel()
positioner2->set_size(200, 200);
positioner2->set_anchor_rect(anchorRect2.x(), anchorRect2.y(), anchorRect2.width(), anchorRect2.height());
positioner2->set_constraint_adjustment(Test::XdgPositioner::constraint_adjustment_slide_x | Test::XdgPositioner::constraint_adjustment_slide_y);
transientShellSurface = Test::createXdgPopupSurface(transientSurface.get(), parentShellSurface->xdgSurface(), positioner2.get());
transientShellSurface.reset(Test::createXdgPopupSurface(transientSurface.get(), parentShellSurface->xdgSurface(), positioner2.get()));
transient = Test::renderAndWaitForShown(transientSurface.get(), QSize(200, 200), Qt::red);
QVERIFY(transient);

View File

@ -9,15 +9,12 @@
#include "kwin_wayland_test.h"
#include "main.h"
#include "utils/xcbutils.h"
#include "virtualdesktops.h"
#include "wayland_server.h"
#include "window.h"
#include "workspace.h"
#if KWIN_BUILD_X11
#include "utils/xcbutils.h"
#endif
#include <KWayland/Client/surface.h>
using namespace KWin;
@ -31,9 +28,8 @@ private Q_SLOTS:
void initTestCase();
void init();
void cleanup();
#if KWIN_BUILD_X11
void testNetCurrentDesktop();
#endif
void testLastDesktopRemoved();
void testWindowOnMultipleDesktops();
void testRemoveDesktopWithWindow();
@ -56,7 +52,6 @@ void VirtualDesktopTest::initTestCase()
kwinApp()->start();
QVERIFY(applicationStartedSpy.wait());
#if KWIN_BUILD_X11
if (kwinApp()->x11Connection()) {
// verify the current desktop x11 property on startup, see BUG: 391034
Xcb::Atom currentDesktopAtom("_NET_CURRENT_DESKTOP");
@ -66,7 +61,6 @@ void VirtualDesktopTest::initTestCase()
QCOMPARE(currentDesktop.value(0, &ok), 0);
QVERIFY(ok);
}
#endif
}
void VirtualDesktopTest::init()
@ -81,7 +75,6 @@ void VirtualDesktopTest::cleanup()
Test::destroyWaylandConnection();
}
#if KWIN_BUILD_X11
void VirtualDesktopTest::testNetCurrentDesktop()
{
if (!kwinApp()->x11Connection()) {
@ -122,7 +115,6 @@ void VirtualDesktopTest::testNetCurrentDesktop()
QCOMPARE(currentDesktop.value(0, &ok), 0);
QVERIFY(ok);
}
#endif
void VirtualDesktopTest::testLastDesktopRemoved()
{

File diff suppressed because it is too large Load Diff

View File

@ -1,283 +0,0 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2024 David Edmundson <davidedmundson@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "kwin_wayland_test.h"
#include "input.h"
#include "options.h"
#include "pointer_input.h"
#include "qabstracteventdispatcher.h"
#include "qsocketnotifier.h"
#include "wayland/keyboard.h"
#include "wayland/seat.h"
#include "wayland_server.h"
#include "workspace.h"
#include <KConfigGroup>
#include <linux/input.h>
#define explicit xcb_explicit
#include <xcb/xcb.h>
#include <xcb/xcbext.h>
#include <xcb/xinput.h>
#include <xcb/xkb.h>
#undef explicit
using namespace KWin;
static const QString s_socketName = QStringLiteral("wayland_test_kwin_x11-key-read-0");
enum class State {
Press,
Release
} state;
typedef QPair<State, int> KeyAction;
Q_DECLARE_METATYPE(KeyAction);
/*
* This tests the "Legacy App Support" feature of allowing X11 apps to get notified of some key press events
*/
class X11KeyReadTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase_data();
void initTestCase();
void init();
void cleanup();
void testSimpleLetter();
void onlyModifier();
void letterWithModifier();
private:
QList<KeyAction> recievedX11EventsForInput(const QList<KeyAction> &keyEventsIn);
};
void X11KeyReadTest::initTestCase_data()
{
QTest::addColumn<XwaylandEavesdropsMode>("operatingMode");
QTest::newRow("all") << XwaylandEavesdropsMode::All;
QTest::newRow("allWithModifier") << XwaylandEavesdropsMode::AllKeysWithModifier;
QTest::newRow("nonCharacter") << XwaylandEavesdropsMode::NonCharacterKeys;
QTest::newRow("none") << XwaylandEavesdropsMode::None;
}
void X11KeyReadTest::initTestCase()
{
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
QVERIFY(waylandServer()->init(s_socketName));
Test::setOutputConfig({
QRect(0, 0, 1280, 1024),
});
qputenv("KWIN_XKB_DEFAULT_KEYMAP", "1");
qputenv("XKB_DEFAULT_RULES", "evdev");
kwinApp()->start();
QVERIFY(applicationStartedSpy.wait());
Test::XcbConnectionPtr c = Test::createX11Connection();
QVERIFY(!xcb_connection_has_error(c.get()));
}
void X11KeyReadTest::init()
{
QFETCH_GLOBAL(XwaylandEavesdropsMode, operatingMode);
options->setXwaylandEavesdrops(operatingMode);
workspace()->setActiveOutput(QPoint(640, 512));
KWin::input()->pointer()->warp(QPoint(640, 512));
}
void X11KeyReadTest::cleanup()
{
}
void X11KeyReadTest::testSimpleLetter()
{
// press a
QList<KeyAction> keyEvents = {
{State::Press, KEY_A},
{State::Release, KEY_A},
};
auto received = recievedX11EventsForInput(keyEvents);
QList<KeyAction> expected;
QFETCH_GLOBAL(XwaylandEavesdropsMode, operatingMode);
switch (operatingMode) {
case XwaylandEavesdropsMode::None:
case XwaylandEavesdropsMode::NonCharacterKeys:
case XwaylandEavesdropsMode::AllKeysWithModifier:
expected = {};
break;
case XwaylandEavesdropsMode::All:
expected = keyEvents;
break;
}
QCOMPARE(received, expected);
}
void X11KeyReadTest::onlyModifier()
{
QList<KeyAction> keyEvents = {
{State::Press, KEY_LEFTALT},
{State::Release, KEY_LEFTALT},
};
auto received = recievedX11EventsForInput(keyEvents);
QList<KeyAction> expected;
QFETCH_GLOBAL(XwaylandEavesdropsMode, operatingMode);
switch (operatingMode) {
case XwaylandEavesdropsMode::None:
expected = {};
break;
case XwaylandEavesdropsMode::NonCharacterKeys:
case XwaylandEavesdropsMode::AllKeysWithModifier:
case XwaylandEavesdropsMode::All:
expected = keyEvents;
break;
}
QCOMPARE(received, expected);
}
void X11KeyReadTest::letterWithModifier()
{
QList<KeyAction> keyEvents = {
{State::Press, KEY_LEFTALT},
{State::Press, KEY_F},
{State::Release, KEY_F},
{State::Release, KEY_LEFTALT},
};
auto received = recievedX11EventsForInput(keyEvents);
QList<KeyAction> expected;
QFETCH_GLOBAL(XwaylandEavesdropsMode, operatingMode);
switch (operatingMode) {
case XwaylandEavesdropsMode::None:
expected = {};
break;
case XwaylandEavesdropsMode::NonCharacterKeys:
expected = {
{State::Press, KEY_LEFTALT},
{State::Release, KEY_LEFTALT},
};
break;
case XwaylandEavesdropsMode::AllKeysWithModifier:
case XwaylandEavesdropsMode::All:
expected = keyEvents;
break;
}
QCOMPARE(received, expected);
}
class X11EventRecorder :
public
QObject
{
Q_OBJECT
public:
X11EventRecorder(xcb_connection_t * c);
QList<KeyAction> keyEvents() const
{
return m_keyEvents;
}
Q_SIGNALS:
void fenceReceived();
private:
void processXcbEvents();
QList<KeyAction> m_keyEvents;
xcb_connection_t *m_connection;
QSocketNotifier *m_notifier;
};
X11EventRecorder::X11EventRecorder(xcb_connection_t * c)
: QObject()
, m_connection(c)
, m_notifier(new QSocketNotifier(xcb_get_file_descriptor(m_connection), QSocketNotifier::Read, this))
{
struct
{
xcb_input_event_mask_t head;
xcb_input_xi_event_mask_t mask;
} mask;
mask.head.deviceid = XCB_INPUT_DEVICE_ALL;
mask.head.mask_len = sizeof(mask.mask) / sizeof(uint32_t);
mask.mask = static_cast<xcb_input_xi_event_mask_t>(
XCB_INPUT_XI_EVENT_MASK_KEY_PRESS
| XCB_INPUT_XI_EVENT_MASK_KEY_RELEASE
| XCB_INPUT_RAW_KEY_PRESS
| XCB_INPUT_RAW_KEY_RELEASE);
xcb_input_xi_select_events(c, kwinApp()->x11RootWindow(), 1, &mask.head);
xcb_flush(c);
connect(m_notifier, &QSocketNotifier::activated, this, &X11EventRecorder::processXcbEvents);
connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, &X11EventRecorder::processXcbEvents);
connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::awake, this, &X11EventRecorder::processXcbEvents);
}
void X11EventRecorder::processXcbEvents()
{
xcb_generic_event_t *event;
while ((event = xcb_poll_for_event(m_connection))) {
u_int8_t responseType = event->response_type & ~0x80;
if (responseType == XCB_GE_GENERIC) {
auto *geEvent = reinterpret_cast<xcb_ge_generic_event_t *>(event);
if (geEvent->event_type == XCB_INPUT_KEY_PRESS || geEvent->event_type == XCB_INPUT_KEY_RELEASE) {
auto keyEvent = reinterpret_cast<xcb_input_key_press_event_t *>(geEvent);
int nativeKeyCode = keyEvent->detail - 0x08;
if (nativeKeyCode == 0) {
Q_EMIT fenceReceived();
} else {
KeyAction action({geEvent->event_type == XCB_INPUT_KEY_PRESS ? State::Press : State::Release, nativeKeyCode});
m_keyEvents << action;
}
}
}
std::free(event);
}
xcb_flush(m_connection);
}
QList<KeyAction> X11KeyReadTest::recievedX11EventsForInput(const QList<KeyAction> &keysIn)
{
quint32 timestamp = 1;
Test::XcbConnectionPtr c = Test::createX11Connection();
X11EventRecorder eventReader(c.get());
QSignalSpy fenceEventSpy(&eventReader, &X11EventRecorder::fenceReceived);
for (const KeyAction &action : keysIn) {
if (action.first == State::Press) {
Test::keyboardKeyPressed(action.second, timestamp++);
} else {
Test::keyboardKeyReleased(action.second, timestamp++);
}
Test::flushWaylandConnection();
QTest::qWait(5);
}
// special case, explicitly send key 0, to use as a fence
ClientConnection *xwaylandClient = waylandServer()->xWaylandConnection();
waylandServer()->seat()->keyboard()->sendKey(0, KeyboardKeyState::Pressed, xwaylandClient);
Test::flushWaylandConnection();
bool fenceComplete = fenceEventSpy.wait();
Q_ASSERT(fenceComplete);
return eventReader.keyEvents();
}
WAYLANDTEST_MAIN(X11KeyReadTest)
#include "x11keyread.moc"

View File

@ -175,7 +175,6 @@ private:
Window *m_window;
std::unique_ptr<KWayland::Client::Surface> m_surface;
std::unique_ptr<Test::XdgToplevel> m_shellSurface;
std::unique_ptr<Test::XdgToplevelDecorationV1> m_decoration;
std::unique_ptr<QSignalSpy> m_toplevelConfigureRequestedSpy;
std::unique_ptr<QSignalSpy> m_surfaceConfigureRequestedSpy;
@ -238,15 +237,15 @@ void TestXdgShellWindowRules::createTestWindow(ClientFlags flags)
: Test::XdgToplevelDecorationV1::mode_client_side;
// Create an xdg surface.
m_surface = Test::createSurface();
m_shellSurface = Test::createXdgToplevelSurface(m_surface.get(), Test::CreationSetup::CreateOnly);
m_decoration = Test::createXdgToplevelDecorationV1(m_shellSurface.get());
m_shellSurface.reset(Test::createXdgToplevelSurface(m_surface.get(), Test::CreationSetup::CreateOnly, m_surface.get()));
Test::XdgToplevelDecorationV1 *decoration = Test::createXdgToplevelDecorationV1(m_shellSurface.get(), m_shellSurface.get());
// Add signal watchers
m_toplevelConfigureRequestedSpy = std::make_unique<QSignalSpy>(m_shellSurface.get(), &Test::XdgToplevel::configureRequested);
m_surfaceConfigureRequestedSpy = std::make_unique<QSignalSpy>(m_shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
m_shellSurface->set_app_id(QStringLiteral("org.kde.foo"));
m_decoration->set_mode(decorationMode);
decoration->set_mode(decorationMode);
// Wait for the initial configure event
m_surface->commit(KWayland::Client::Surface::CommitFlag::None);
@ -278,7 +277,6 @@ void TestXdgShellWindowRules::destroyTestWindow()
{
m_surfaceConfigureRequestedSpy.reset();
m_toplevelConfigureRequestedSpy.reset();
m_decoration.reset();
m_shellSurface.reset();
m_surface.reset();
QVERIFY(Test::waitForWindowClosed(m_window));
@ -2831,7 +2829,7 @@ void TestXdgShellWindowRules::testScreenDontAffect()
QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
// The user can still move the window to another screen.
m_window->sendToOutput(outputs.at(1));
workspace()->sendWindowToOutput(m_window, outputs.at(1));
QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
destroyTestWindow();
@ -2850,7 +2848,7 @@ void TestXdgShellWindowRules::testScreenApply()
QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
// The user can move the window to another screen.
m_window->sendToOutput(outputs.at(0));
workspace()->sendWindowToOutput(m_window, outputs.at(0));
QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
destroyTestWindow();
@ -2868,12 +2866,11 @@ void TestXdgShellWindowRules::testScreenRemember()
QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
// Move the window to the second screen.
m_window->sendToOutput(outputs.at(1));
workspace()->sendWindowToOutput(m_window, outputs.at(1));
QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
// Close and reopen the window.
destroyTestWindow();
workspace()->setActiveOutput(outputs.at(0));
createTestWindow();
QEXPECT_FAIL("", "Applying a screen rule on a new client fails on Wayland", Continue);
@ -2895,7 +2892,7 @@ void TestXdgShellWindowRules::testScreenForce()
QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
// User should not be able to move the window to another screen.
m_window->sendToOutput(outputs.at(0));
workspace()->sendWindowToOutput(m_window, outputs.at(0));
QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
// Disable the output where the window is on, so the window is moved the other screen
@ -2937,7 +2934,7 @@ void TestXdgShellWindowRules::testScreenApplyNow()
QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
// The user can move the window to another screen.
m_window->sendToOutput(outputs.at(0));
workspace()->sendWindowToOutput(m_window, outputs.at(0));
QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
// The rule should not be applied again.
@ -2959,12 +2956,11 @@ void TestXdgShellWindowRules::testScreenForceTemporarily()
QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
// User is not allowed to move it
m_window->sendToOutput(outputs.at(0));
workspace()->sendWindowToOutput(m_window, outputs.at(0));
QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
// Close and reopen the window.
destroyTestWindow();
workspace()->setActiveOutput(outputs.at(0));
createTestWindow();
// The rule should be discarded now

View File

@ -68,14 +68,11 @@ private Q_SLOTS:
void testMaximizedToFullscreen_data();
void testMaximizedToFullscreen();
void testSendMaximizedWindowToAnotherOutput();
void testInteractiveMoveUnmaximizeFull();
void testInteractiveMoveUnmaximizeInitiallyFull();
void testInteractiveMoveUnmaximizeHorizontal();
void testInteractiveMoveUnmaximizeVertical();
void testFullscreenMultipleOutputs();
void testHidden();
void testDesktopFileName();
void testCaptionSimplified();
void testCaptionMultipleWindows();
void testUnresponsiveWindow_data();
void testUnresponsiveWindow();
void testAppMenu();
@ -104,9 +101,6 @@ private Q_SLOTS:
void testMaximizeAndChangeDecorationModeAfterInitialCommit();
void testFullScreenAndChangeDecorationModeAfterInitialCommit();
void testChangeDecorationModeAfterInitialCommit();
void testModal();
void testCloseModal();
void testCloseInactiveModal();
};
void TestXdgShellWindow::testXdgPopupReactive_data()
@ -209,7 +203,7 @@ void TestXdgShellWindow::initTestCase()
void TestXdgShellWindow::init()
{
QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Seat | Test::AdditionalWaylandInterface::XdgDecorationV1 | Test::AdditionalWaylandInterface::AppMenu | Test::AdditionalWaylandInterface::XdgDialogV1));
QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Seat | Test::AdditionalWaylandInterface::XdgDecorationV1 | Test::AdditionalWaylandInterface::AppMenu));
QVERIFY(Test::waitForWaylandPointer());
workspace()->setActiveOutput(QPoint(640, 512));
@ -481,7 +475,7 @@ void TestXdgShellWindow::testSendFullScreenWindowToAnotherOutput()
QCOMPARE(window->output(), outputs[0]);
// Send the window to another output.
window->sendToOutput(outputs[1]);
workspace()->sendWindowToOutput(window, outputs[1]);
QCOMPARE(window->isFullScreen(), true);
QCOMPARE(window->frameGeometry(), QRectF(1280, 0, 1280, 1024));
QCOMPARE(window->fullscreenGeometryRestore(), QRectF(1280 + 10, 20, 100, 50));
@ -714,6 +708,52 @@ void TestXdgShellWindow::testCaptionSimplified()
QCOMPARE(window->caption(), origTitle.simplified());
}
void TestXdgShellWindow::testCaptionMultipleWindows()
{
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
shellSurface->set_title(QStringLiteral("foo"));
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
QVERIFY(window);
QCOMPARE(window->caption(), QStringLiteral("foo"));
QCOMPARE(window->captionNormal(), QStringLiteral("foo"));
QCOMPARE(window->captionSuffix(), QString());
std::unique_ptr<KWayland::Client::Surface> surface2(Test::createSurface());
std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get()));
shellSurface2->set_title(QStringLiteral("foo"));
auto c2 = Test::renderAndWaitForShown(surface2.get(), QSize(100, 50), Qt::blue);
QVERIFY(c2);
QCOMPARE(c2->caption(), QStringLiteral("foo <2>"));
QCOMPARE(c2->captionNormal(), QStringLiteral("foo"));
QCOMPARE(c2->captionSuffix(), QStringLiteral(" <2>"));
std::unique_ptr<KWayland::Client::Surface> surface3(Test::createSurface());
std::unique_ptr<Test::XdgToplevel> shellSurface3(Test::createXdgToplevelSurface(surface3.get()));
shellSurface3->set_title(QStringLiteral("foo"));
auto c3 = Test::renderAndWaitForShown(surface3.get(), QSize(100, 50), Qt::blue);
QVERIFY(c3);
QCOMPARE(c3->caption(), QStringLiteral("foo <3>"));
QCOMPARE(c3->captionNormal(), QStringLiteral("foo"));
QCOMPARE(c3->captionSuffix(), QStringLiteral(" <3>"));
std::unique_ptr<KWayland::Client::Surface> surface4(Test::createSurface());
std::unique_ptr<Test::XdgToplevel> shellSurface4(Test::createXdgToplevelSurface(surface4.get()));
shellSurface4->set_title(QStringLiteral("bar"));
auto c4 = Test::renderAndWaitForShown(surface4.get(), QSize(100, 50), Qt::blue);
QVERIFY(c4);
QCOMPARE(c4->caption(), QStringLiteral("bar"));
QCOMPARE(c4->captionNormal(), QStringLiteral("bar"));
QCOMPARE(c4->captionSuffix(), QString());
QSignalSpy captionChangedSpy(c4, &Window::captionChanged);
shellSurface4->set_title(QStringLiteral("foo"));
QVERIFY(captionChangedSpy.wait());
QCOMPARE(captionChangedSpy.count(), 1);
QCOMPARE(c4->caption(), QStringLiteral("foo <4>"));
QCOMPARE(c4->captionNormal(), QStringLiteral("foo"));
QCOMPARE(c4->captionSuffix(), QStringLiteral(" <4>"));
}
void TestXdgShellWindow::testUnresponsiveWindow_data()
{
QTest::addColumn<QString>("shellInterface"); // see env selection in qwaylandintegration.cpp
@ -1795,321 +1835,13 @@ void TestXdgShellWindow::testSendMaximizedWindowToAnotherOutput()
QCOMPARE(window->output(), outputs[0]);
// Send the window to another output.
window->sendToOutput(outputs[1]);
workspace()->sendWindowToOutput(window, outputs[1]);
QCOMPARE(window->maximizeMode(), MaximizeFull);
QCOMPARE(window->frameGeometry(), QRectF(1280, 0, 1280, 1024));
QCOMPARE(window->geometryRestore(), QRectF(1280 + 10, 20, 100, 50));
QCOMPARE(window->output(), outputs[1]);
}
void TestXdgShellWindow::testInteractiveMoveUnmaximizeFull()
{
// This test verifies that a maximized xdg-toplevel is going to be properly unmaximized when it's dragged.
// Create the window.
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
// Wait for the compositor to send a configure event with the activated state.
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
QVERIFY(surfaceConfigureRequestedSpy.wait());
// Make the window maximized.
const QRectF originalGeometry = window->frameGeometry();
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
window->maximize(MaximizeFull);
QVERIFY(surfaceConfigureRequestedSpy.wait());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->maximizeMode(), MaximizeFull);
QCOMPARE(window->requestedMaximizeMode(), MaximizeFull);
// Start interactive move.
QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
const qreal xOffset = 0.25;
const qreal yOffset = 0.5;
quint32 timestamp = 0;
Test::pointerMotion(QPointF(window->x() + window->width() * xOffset, window->y() + window->height() * yOffset), timestamp++);
window->performMouseCommand(Options::MouseMove, input()->pointer()->pos());
QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeFull);
QCOMPARE(window->requestedMaximizeMode(), MaximizeFull);
// Move the window to unmaximize it.
const QRectF maximizedGeometry = window->frameGeometry();
Test::pointerMotionRelative(QPointF(0, 100), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 0);
QCOMPARE(window->maximizeMode(), MaximizeFull);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), maximizedGeometry);
// Move the window a tiny bit more.
Test::pointerMotionRelative(QPointF(0, 10), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 0);
QCOMPARE(window->maximizeMode(), MaximizeFull);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), maximizedGeometry);
// Render the window at the new size.
QVERIFY(surfaceConfigureRequestedSpy.wait());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->maximizeMode(), MaximizeRestore);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), QRectF(input()->pointer()->pos() - QPointF(originalGeometry.width() * xOffset, originalGeometry.height() * yOffset), originalGeometry.size()));
// Move the window again.
const QRectF normalGeometry = window->frameGeometry();
Test::pointerMotionRelative(QPointF(0, 10), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeRestore);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), normalGeometry.translated(0, 10));
// Finish interactive move.
window->keyPressEvent(Qt::Key_Enter);
QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
}
void TestXdgShellWindow::testInteractiveMoveUnmaximizeInitiallyFull()
{
// This test verifies that an initially maximized xdg-toplevel will be properly unmaximized when it's dragged.
// Create the window.
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), [](Test::XdgToplevel *toplevel) {
toplevel->set_maximized();
}));
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
// Wait for the compositor to send a configure event with the activated state.
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
QVERIFY(surfaceConfigureRequestedSpy.wait());
// Start interactive move.
QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
const qreal xOffset = 0.25;
const qreal yOffset = 0.5;
quint32 timestamp = 0;
Test::pointerMotion(QPointF(window->x() + window->width() * xOffset, window->y() + window->height() * yOffset), timestamp++);
window->performMouseCommand(Options::MouseMove, input()->pointer()->pos());
QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeFull);
QCOMPARE(window->requestedMaximizeMode(), MaximizeFull);
// Move the window to unmaximize it.
const QRectF maximizedGeometry = window->frameGeometry();
Test::pointerMotionRelative(QPointF(0, 100), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 0);
QCOMPARE(window->maximizeMode(), MaximizeFull);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), maximizedGeometry);
// Move the window a tiny bit more.
Test::pointerMotionRelative(QPointF(0, 10), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 0);
QCOMPARE(window->maximizeMode(), MaximizeFull);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), maximizedGeometry);
// Render the window at the new size.
const QSize restoredSize(100, 50);
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), QSize(0, 0));
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), restoredSize, Qt::red);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->maximizeMode(), MaximizeRestore);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), QRectF(input()->pointer()->pos() - QPointF(restoredSize.width() * xOffset, restoredSize.height() * yOffset), restoredSize));
// Move the window again.
const QRectF normalGeometry = window->frameGeometry();
Test::pointerMotionRelative(QPointF(0, 10), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeRestore);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), normalGeometry.translated(0, 10));
// Finish interactive move.
window->keyPressEvent(Qt::Key_Enter);
QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
}
void TestXdgShellWindow::testInteractiveMoveUnmaximizeHorizontal()
{
// This test verifies that a maximized horizontally xdg-toplevel is going to be properly unmaximized when it's dragged horizontally.
// Create the window.
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
// Wait for the compositor to send a configure event with the activated state.
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
QVERIFY(surfaceConfigureRequestedSpy.wait());
// Make the window maximized.
const QRectF originalGeometry = window->frameGeometry();
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
window->maximize(MaximizeHorizontal);
QVERIFY(surfaceConfigureRequestedSpy.wait());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->maximizeMode(), MaximizeHorizontal);
QCOMPARE(window->requestedMaximizeMode(), MaximizeHorizontal);
// Start interactive move.
QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
const qreal xOffset = 0.25;
const qreal yOffset = 0.5;
quint32 timestamp = 0;
Test::pointerMotion(QPointF(window->x() + window->width() * xOffset, window->y() + window->height() * yOffset), timestamp++);
window->performMouseCommand(Options::MouseMove, input()->pointer()->pos());
QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeHorizontal);
QCOMPARE(window->requestedMaximizeMode(), MaximizeHorizontal);
// Move the window vertically, it's not going to be unmaximized.
const QRectF maximizedGeometry = window->frameGeometry();
Test::pointerMotionRelative(QPointF(0, 100), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeHorizontal);
QCOMPARE(window->requestedMaximizeMode(), MaximizeHorizontal);
QCOMPARE(window->frameGeometry(), maximizedGeometry.translated(0, 100));
// Move the window horizontally.
Test::pointerMotionRelative(QPointF(100, 0), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeHorizontal);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), maximizedGeometry.translated(0, 100));
// Move the window to the right a bit more.
Test::pointerMotionRelative(QPointF(10, 0), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeHorizontal);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), maximizedGeometry.translated(0, 100));
// Render the window at the new size.
QVERIFY(surfaceConfigureRequestedSpy.wait());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->maximizeMode(), MaximizeRestore);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), QRectF(input()->pointer()->pos() - QPointF(originalGeometry.width() * xOffset, originalGeometry.height() * yOffset), originalGeometry.size()));
// Move the window again.
const QRectF normalGeometry = window->frameGeometry();
Test::pointerMotionRelative(QPointF(10, 0), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 2);
QCOMPARE(window->maximizeMode(), MaximizeRestore);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), normalGeometry.translated(10, 0));
// Finish interactive move.
window->keyPressEvent(Qt::Key_Enter);
QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
}
void TestXdgShellWindow::testInteractiveMoveUnmaximizeVertical()
{
// This test verifies that a maximized vertically xdg-toplevel is going to be properly unmaximized when it's dragged vertically.
// Create the window.
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
// Wait for the compositor to send a configure event with the activated state.
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
QVERIFY(surfaceConfigureRequestedSpy.wait());
// Make the window maximized.
const QRectF originalGeometry = window->frameGeometry();
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
window->maximize(MaximizeVertical);
QVERIFY(surfaceConfigureRequestedSpy.wait());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->maximizeMode(), MaximizeVertical);
QCOMPARE(window->requestedMaximizeMode(), MaximizeVertical);
// Start interactive move.
QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
const qreal xOffset = 0.25;
const qreal yOffset = 0.5;
quint32 timestamp = 0;
Test::pointerMotion(QPointF(window->x() + window->width() * xOffset, window->y() + window->height() * yOffset), timestamp++);
window->performMouseCommand(Options::MouseMove, input()->pointer()->pos());
QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeVertical);
QCOMPARE(window->requestedMaximizeMode(), MaximizeVertical);
// Move the window to the right, it's not going to be unmaximized.
const QRectF maximizedGeometry = window->frameGeometry();
Test::pointerMotionRelative(QPointF(100, 0), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeVertical);
QCOMPARE(window->requestedMaximizeMode(), MaximizeVertical);
QCOMPARE(window->frameGeometry(), maximizedGeometry.translated(100, 0));
// Move the window vertically.
Test::pointerMotionRelative(QPointF(0, 100), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeVertical);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), maximizedGeometry.translated(100, 0));
// Move the window down a bit more.
Test::pointerMotionRelative(QPointF(0, 10), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
QCOMPARE(window->maximizeMode(), MaximizeVertical);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), maximizedGeometry.translated(100, 0));
// Render the window at the new size.
QVERIFY(surfaceConfigureRequestedSpy.wait());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->maximizeMode(), MaximizeRestore);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), QRectF(input()->pointer()->pos() - QPointF(originalGeometry.width() * xOffset, originalGeometry.height() * yOffset), originalGeometry.size()));
// Move the window again.
const QRectF normalGeometry = window->frameGeometry();
Test::pointerMotionRelative(QPointF(0, 10), timestamp++);
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 2);
QCOMPARE(window->maximizeMode(), MaximizeRestore);
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
QCOMPARE(window->frameGeometry(), normalGeometry.translated(0, 10));
// Finish interactive move.
window->keyPressEvent(Qt::Key_Enter);
QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
}
void TestXdgShellWindow::testMaximizeAndChangeDecorationModeAfterInitialCommit()
{
// Ideally, the app would initialize the xdg-toplevel surface before the initial commit, but
@ -2193,134 +1925,5 @@ void TestXdgShellWindow::testChangeDecorationModeAfterInitialCommit()
QCOMPARE(decorationConfigureRequestedSpy.last().at(0).value<Test::XdgToplevelDecorationV1::mode>(), Test::XdgToplevelDecorationV1::mode_client_side);
}
void TestXdgShellWindow::testModal()
{
auto parentSurface = Test::createSurface();
auto parentToplevel = Test::createXdgToplevelSurface(parentSurface.get());
auto parentWindow = Test::renderAndWaitForShown(parentSurface.get(), {200, 200}, Qt::cyan);
QVERIFY(parentWindow);
auto childSurface = Test::createSurface();
auto childToplevel = Test::createXdgToplevelSurface(childSurface.get(), [&parentToplevel](Test::XdgToplevel *toplevel) {
toplevel->set_parent(parentToplevel->object());
});
auto childWindow = Test::renderAndWaitForShown(childSurface.get(), {200, 200}, Qt::yellow);
QVERIFY(childWindow);
QVERIFY(!childWindow->isModal());
QCOMPARE(childWindow->transientFor(), parentWindow);
auto dialog = Test::createXdgDialogV1(childToplevel.get());
QVERIFY(Test::waylandSync());
QVERIFY(dialog);
QVERIFY(!childWindow->isModal());
QSignalSpy modalChangedSpy(childWindow, &Window::modalChanged);
dialog->set_modal();
Test::flushWaylandConnection();
QVERIFY(modalChangedSpy.wait());
QVERIFY(childWindow->isModal());
dialog->unset_modal();
Test::flushWaylandConnection();
QVERIFY(modalChangedSpy.wait());
QVERIFY(!childWindow->isModal());
dialog->set_modal();
Test::flushWaylandConnection();
QVERIFY(modalChangedSpy.wait());
Workspace::self()->activateWindow(parentWindow);
QCOMPARE(Workspace::self()->activeWindow(), childWindow);
dialog.reset();
Test::flushWaylandConnection();
QVERIFY(modalChangedSpy.wait());
QVERIFY(!childWindow->isModal());
}
void TestXdgShellWindow::testCloseModal()
{
// This test verifies that the parent window will be activated when an active modal dialog is closed.
// Create a parent and a child windows.
auto parentSurface = Test::createSurface();
auto parentToplevel = Test::createXdgToplevelSurface(parentSurface.get());
auto parent = Test::renderAndWaitForShown(parentSurface.get(), {200, 200}, Qt::cyan);
QVERIFY(parent);
auto childSurface = Test::createSurface();
auto childToplevel = Test::createXdgToplevelSurface(childSurface.get(), [&parentToplevel](Test::XdgToplevel *toplevel) {
toplevel->set_parent(parentToplevel->object());
});
auto child = Test::renderAndWaitForShown(childSurface.get(), {200, 200}, Qt::yellow);
QVERIFY(child);
QVERIFY(!child->isModal());
QCOMPARE(child->transientFor(), parent);
// Set modal state.
auto dialog = Test::createXdgDialogV1(childToplevel.get());
QSignalSpy modalChangedSpy(child, &Window::modalChanged);
dialog->set_modal();
Test::flushWaylandConnection();
QVERIFY(modalChangedSpy.wait());
QVERIFY(child->isModal());
QCOMPARE(workspace()->activeWindow(), child);
// Close the child.
QSignalSpy childClosedSpy(child, &Window::closed);
childToplevel.reset();
childSurface.reset();
dialog.reset();
Test::flushWaylandConnection();
QVERIFY(childClosedSpy.wait());
QCOMPARE(workspace()->activeWindow(), parent);
}
void TestXdgShellWindow::testCloseInactiveModal()
{
// This test verifies that the parent window will not be activated when an inactive modal dialog is closed.
// Create a parent and a child windows.
auto parentSurface = Test::createSurface();
auto parentToplevel = Test::createXdgToplevelSurface(parentSurface.get());
auto parent = Test::renderAndWaitForShown(parentSurface.get(), {200, 200}, Qt::cyan);
QVERIFY(parent);
auto childSurface = Test::createSurface();
auto childToplevel = Test::createXdgToplevelSurface(childSurface.get(), [&parentToplevel](Test::XdgToplevel *toplevel) {
toplevel->set_parent(parentToplevel->object());
});
auto child = Test::renderAndWaitForShown(childSurface.get(), {200, 200}, Qt::yellow);
QVERIFY(child);
QVERIFY(!child->isModal());
QCOMPARE(child->transientFor(), parent);
// Set modal state.
auto dialog = Test::createXdgDialogV1(childToplevel.get());
QSignalSpy modalChangedSpy(child, &Window::modalChanged);
dialog->set_modal();
Test::flushWaylandConnection();
QVERIFY(modalChangedSpy.wait());
QVERIFY(child->isModal());
QCOMPARE(workspace()->activeWindow(), child);
// Show another window.
auto otherSurface = Test::createSurface();
auto otherToplevel = Test::createXdgToplevelSurface(otherSurface.get());
auto otherWindow = Test::renderAndWaitForShown(otherSurface.get(), {200, 200}, Qt::magenta);
QVERIFY(otherWindow);
workspace()->setActiveWindow(otherWindow);
QCOMPARE(workspace()->activeWindow(), otherWindow);
// Close the child.
QSignalSpy childClosedSpy(child, &Window::closed);
childToplevel.reset();
childSurface.reset();
dialog.reset();
Test::flushWaylandConnection();
QVERIFY(childClosedSpy.wait());
QCOMPARE(workspace()->activeWindow(), otherWindow);
}
WAYLANDTEST_MAIN(TestXdgShellWindow)
#include "xdgshellwindow_test.moc"

View File

@ -19,8 +19,6 @@ class XkbTest : public QObject
private Q_SLOTS:
void testToQtKey_data();
void testToQtKey();
void testFromQtKey_data();
void testFromQtKey();
};
// from kwindowsystem/src/platforms/xcb/kkeyserver.cpp
@ -500,31 +498,5 @@ void XkbTest::testToQtKey()
QTEST(xkb.toQtKey(keySym), "qt");
}
void XkbTest::testFromQtKey_data()
{
QTest::addColumn<xkb_keysym_t>("keySym");
QTest::addColumn<int>("keyQt");
for (std::size_t i = 0; i < sizeof(g_rgQtToSymX) / sizeof(TransKey); i++) {
const QByteArray row = QByteArray::number(g_rgQtToSymX[i].keySymX, 16);
QTest::newRow(row.constData()) << g_rgQtToSymX[i].keySymX << (g_rgQtToSymX[i].keySymQt | g_rgQtToSymX[i].modifiers).toCombined();
}
}
void XkbTest::testFromQtKey()
{
Xkb xkb;
QFETCH(xkb_keysym_t, keySym);
QFETCH(int, keyQt);
QList<xkb_keysym_t> keys = xkb.keysymsFromQtKey(keyQt);
QEXPECT_FAIL(QByteArray::number(XKB_KEY_Hyper_L, 16), "keysymsFromQtKey doesn't map hyper to meta", Continue);
QEXPECT_FAIL(QByteArray::number(XKB_KEY_Hyper_R, 16), "keysymsFromQtKey doesn't map hyper to meta", Continue);
#if QT_VERSION < QT_VERSION_CHECK(6, 7, 1)
QEXPECT_FAIL(QByteArray::number(XKB_KEY_KP_Equal, 16), "KP_Equal is not correctly identified as keypad key in Qt 6.7.0; fixed in 6.7.1: https://codereview.qt-project.org/c/qt/qtbase/+/546889", Continue);
#endif
QVERIFY(keys.contains(keySym));
}
QTEST_MAIN(XkbTest)
#include "test_xkb.moc"

View File

@ -17,11 +17,6 @@ uint32_t Xcb::toXNative(qreal value)
return value;
}
QRect Xcb::toXNative(const QRectF &rect)
{
return rect.toRect();
}
qreal Xcb::fromXNative(int value)
{
return value;

View File

@ -1,37 +0,0 @@
# SPDX-FileCopyrightText: 2024 David Redondo <kde@david-redono.de>
# SPDX-License-Identifier: BSD-3-Clause
find_package(PkgConfig)
pkg_check_modules(PKG_Libeis QUIET libeis-1.0)
find_path(Libeis_INCLUDE_DIR
NAMES libeis.h
HINTS ${PKG_Libeis_INCLUDE_DIRS}
)
find_library(Libeis_LIBRARY
NAMES eis
PATHS ${PKG_Libeis_LIBRARY_DIRS}
)
set(Libeis_VERSION ${PKG_Libeis_VERSION})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Libeis-1.0
FOUND_VAR Libeis-1.0_FOUND
REQUIRED_VARS
Libeis_LIBRARY
Libeis_INCLUDE_DIR
VERSION_VAR Libeis_VERSION
)
if(Libeis-1.0_FOUND AND NOT TARGET Libeis::Libeis)
add_library(Libeis::Libeis UNKNOWN IMPORTED)
set_target_properties(Libeis::Libeis PROPERTIES
IMPORTED_LOCATION "${Libeis_LIBRARY}"
INTERFACE_COMPILE_OPTIONS "${PKG_Libeis_CFLAGS_OTHER}"
INTERFACE_INCLUDE_DIRECTORIES "${Libeis_INCLUDE_DIR}"
)
endif()
mark_as_advanced(Libeis_INCLUDE_DIR Libeis_LIBRARY)

View File

@ -12,8 +12,6 @@
# The version of Xwayland
# ``Xwayland_HAVE_LISTENFD``
# True if (the requested version of) Xwayland has -listenfd option
# ``Xwayland_HAVE_ENABLE_EI_PORTAL``
# True if (the requested version of) Xwayland has -enable-ei-portal option
#=============================================================================
# SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
@ -27,7 +25,6 @@ pkg_check_modules(PKG_xwayland QUIET xwayland)
set(Xwayland_VERSION ${PKG_xwayland_VERSION})
pkg_get_variable(Xwayland_HAVE_LISTENFD xwayland have_listenfd)
pkg_get_variable(Xwayland_HAVE_ENABLE_EI_PORTAL xwayland have_enable_ei_portal)
find_program(Xwayland_EXECUTABLE NAMES Xwayland)
find_package_handle_standard_args(Xwayland

Binary file not shown.

Before

Width:  |  Height:  |  Size: 379 B

After

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 609 B

After

Width:  |  Height:  |  Size: 611 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 874 B

After

Width:  |  Height:  |  Size: 877 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 412 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 491 B

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 354 B

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 163 KiB

View File

@ -284,6 +284,15 @@ This controls the behavior of window focus with multiple screens. Note that thes
<variablelist>
<varlistentry>
<term><guilabel>Active screen follows mouse</guilabel></term>
<listitem>
<para>
When this option is enabled, the active screen (where new windows appear, for example) is the screen containing the mouse pointer. When disabled, the active screen is the screen containing the focused window.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><guilabel>Separate screen focus</guilabel></term>
<listitem>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Some files were not shown because too many files have changed in this diff Show More