internal_window.cpp 27.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.

Copyright (C) 2016 Martin Gräßlin <mgraesslin@kde.org>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "kwin_wayland_test.h"
21
#include "platform.h"
22
#include "cursor.h"
23
#include "effects.h"
24
#include "internal_client.h"
25
#include "screens.h"
26
#include "xdgshellclient.h"
27
28
29
30
31
32
#include "wayland_server.h"
#include "workspace.h"

#include <QPainter>
#include <QRasterWindow>

33
34
35
#include <KWayland/Client/keyboard.h>
#include <KWayland/Client/surface.h>
#include <KWayland/Client/seat.h>
36
#include <KWindowSystem>
37

38
39
#include <KWayland/Server/surface_interface.h>

40
41
#include <linux/input.h>

42
43
using namespace KWayland::Client;

44
45
Q_DECLARE_METATYPE(NET::WindowType);

46
47
48
49
50
51
52
53
54
55
56
namespace KWin
{

static const QString s_socketName = QStringLiteral("wayland_test_kwin_internal_window-0");

class InternalWindowTest : public QObject
{
    Q_OBJECT
private Q_SLOTS:
    void initTestCase();
    void init();
57
    void cleanup();
58
59
60
    void testEnterLeave();
    void testPointerPressRelease();
    void testPointerAxis();
61
    void testKeyboard_data();
62
    void testKeyboard();
63
    void testKeyboardShowWithoutActivating();
64
    void testKeyboardTriggersLeave();
65
    void testTouch();
66
    void testOpacity();
67
    void testMove();
68
69
    void testSkipCloseAnimation_data();
    void testSkipCloseAnimation();
70
71
    void testModifierClickUnrestrictedMove();
    void testModifierScroll();
72
    void testPopup();
73
    void testScale();
74
75
76
77
78
    void testWindowType_data();
    void testWindowType();
    void testChangeWindowType_data();
    void testChangeWindowType();
    void testEffectWindow();
79
80
81
82
83
84
85
};

class HelperWindow : public QRasterWindow
{
    Q_OBJECT
public:
    HelperWindow();
86
    ~HelperWindow() override;
87

88
89
90
91
92
93
94
    QPoint latestGlobalMousePos() const {
        return m_latestGlobalMousePos;
    }
    Qt::MouseButtons pressedButtons() const {
        return m_pressedButtons;
    }

95
96
97
98
99
100
101
Q_SIGNALS:
    void entered();
    void left();
    void mouseMoved(const QPoint &global);
    void mousePressed();
    void mouseReleased();
    void wheel();
102
103
    void keyPressed();
    void keyReleased();
104
105
106
107
108
109
110
111

protected:
    void paintEvent(QPaintEvent *event) override;
    bool event(QEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;
    void wheelEvent(QWheelEvent *event) override;
112
113
    void keyPressEvent(QKeyEvent *event) override;
    void keyReleaseEvent(QKeyEvent *event) override;
114
115
116
117

private:
    QPoint m_latestGlobalMousePos;
    Qt::MouseButtons m_pressedButtons = Qt::MouseButtons();
118
119
120
121
122
};

HelperWindow::HelperWindow()
    : QRasterWindow(nullptr)
{
123
    setFlags(Qt::FramelessWindowHint);
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
}

HelperWindow::~HelperWindow() = default;

void HelperWindow::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event)
    QPainter p(this);
    p.fillRect(0, 0, width(), height(), Qt::red);
}

bool HelperWindow::event(QEvent *event)
{
    if (event->type() == QEvent::Enter) {
        emit entered();
    }
    if (event->type() == QEvent::Leave) {
        emit left();
    }
    return QRasterWindow::event(event);
}

void HelperWindow::mouseMoveEvent(QMouseEvent *event)
{
148
    m_latestGlobalMousePos = event->globalPos();
149
150
151
152
153
    emit mouseMoved(event->globalPos());
}

void HelperWindow::mousePressEvent(QMouseEvent *event)
{
154
155
    m_latestGlobalMousePos = event->globalPos();
    m_pressedButtons = event->buttons();
156
157
158
159
160
    emit mousePressed();
}

void HelperWindow::mouseReleaseEvent(QMouseEvent *event)
{
161
162
    m_latestGlobalMousePos = event->globalPos();
    m_pressedButtons = event->buttons();
163
164
165
166
167
168
169
170
171
    emit mouseReleased();
}

void HelperWindow::wheelEvent(QWheelEvent *event)
{
    Q_UNUSED(event)
    emit wheel();
}

172
173
174
175
176
177
178
179
180
181
182
183
void HelperWindow::keyPressEvent(QKeyEvent *event)
{
    Q_UNUSED(event)
    emit keyPressed();
}

void HelperWindow::keyReleaseEvent(QKeyEvent *event)
{
    Q_UNUSED(event)
    emit keyReleased();
}

184
185
void InternalWindowTest::initTestCase()
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
186
187
    qRegisterMetaType<KWin::AbstractClient *>();
    qRegisterMetaType<KWin::InternalClient *>();
188
    qRegisterMetaType<KWin::XdgShellClient *>();
189
190
    QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated);
    QVERIFY(workspaceCreatedSpy.isValid());
191
    kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024));
192
    QVERIFY(waylandServer()->init(s_socketName.toLocal8Bit()));
193
    QMetaObject::invokeMethod(kwinApp()->platform(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(int, 2));
194
    kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig));
195
196
197
198
199
200
201
202
203
204
205
206

    kwinApp()->start();
    QVERIFY(workspaceCreatedSpy.wait());
    QCOMPARE(screens()->count(), 2);
    QCOMPARE(screens()->geometry(0), QRect(0, 0, 1280, 1024));
    QCOMPARE(screens()->geometry(1), QRect(1280, 0, 1280, 1024));
    waylandServer()->initWorkspace();
}

void InternalWindowTest::init()
{
    Cursor::setPos(QPoint(1280, 512));
207
    QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Seat));
208
209
210
211
212
213
    QVERIFY(Test::waitForWaylandKeyboard());
}

void InternalWindowTest::cleanup()
{
    Test::destroyWaylandConnection();
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
214
215

    QMetaObject::invokeMethod(kwinApp()->platform(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(int, 2));
216
217
218
219
}

void InternalWindowTest::testEnterLeave()
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
220
    QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded);
221
222
    QVERIFY(clientAddedSpy.isValid());
    HelperWindow win;
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
223
224
    QVERIFY(!workspace()->findInternal(nullptr));
    QVERIFY(!workspace()->findInternal(&win));
225
226
227
    win.setGeometry(0, 0, 100, 100);
    win.show();

228
    QTRY_COMPARE(clientAddedSpy.count(), 1);
229
    QVERIFY(!workspace()->activeClient());
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
230
231
    InternalClient *c = clientAddedSpy.first().first().value<InternalClient *>();
    QVERIFY(c);
232
    QVERIFY(c->isInternal());
233
    QVERIFY(!c->isDecorated());
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
234
    QCOMPARE(workspace()->findInternal(&win), c);
235
    QCOMPARE(c->geometry(), QRect(0, 0, 100, 100));
236
237
    QVERIFY(c->isShown(false));
    QVERIFY(workspace()->xStackingOrder().contains(c));
238
239
240
241
242
243
244
245
246

    QSignalSpy enterSpy(&win, &HelperWindow::entered);
    QVERIFY(enterSpy.isValid());
    QSignalSpy leaveSpy(&win, &HelperWindow::left);
    QVERIFY(leaveSpy.isValid());
    QSignalSpy moveSpy(&win, &HelperWindow::mouseMoved);
    QVERIFY(moveSpy.isValid());

    quint32 timestamp = 1;
247
    kwinApp()->platform()->pointerMotion(QPoint(50, 50), timestamp++);
248
    QTRY_COMPARE(moveSpy.count(), 1);
249

250
    kwinApp()->platform()->pointerMotion(QPoint(60, 50), timestamp++);
251
252
    QTRY_COMPARE(moveSpy.count(), 2);
    QCOMPARE(moveSpy[1].first().toPoint(), QPoint(60, 50));
253

254
    kwinApp()->platform()->pointerMotion(QPoint(101, 50), timestamp++);
255
    QTRY_COMPARE(leaveSpy.count(), 1);
256
257
258
259
260
261
262
263
264
265

    // set a mask on the window
    win.setMask(QRegion(10, 20, 30, 40));
    // outside the mask we should not get an enter
    kwinApp()->platform()->pointerMotion(QPoint(5, 5), timestamp++);
    QVERIFY(!enterSpy.wait(100));
    QCOMPARE(enterSpy.count(), 1);
    // inside the mask we should still get an enter
    kwinApp()->platform()->pointerMotion(QPoint(25, 27), timestamp++);
    QTRY_COMPARE(enterSpy.count(), 2);
266
267
268
269
}

void InternalWindowTest::testPointerPressRelease()
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
270
    QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded);
271
272
273
274
275
276
277
278
279
    QVERIFY(clientAddedSpy.isValid());
    HelperWindow win;
    win.setGeometry(0, 0, 100, 100);
    win.show();
    QSignalSpy pressSpy(&win, &HelperWindow::mousePressed);
    QVERIFY(pressSpy.isValid());
    QSignalSpy releaseSpy(&win, &HelperWindow::mouseReleased);
    QVERIFY(releaseSpy.isValid());

280
    QTRY_COMPARE(clientAddedSpy.count(), 1);
281
282

    quint32 timestamp = 1;
283
    kwinApp()->platform()->pointerMotion(QPoint(50, 50), timestamp++);
284

285
    kwinApp()->platform()->pointerButtonPressed(BTN_LEFT, timestamp++);
286
    QTRY_COMPARE(pressSpy.count(), 1);
287
    kwinApp()->platform()->pointerButtonReleased(BTN_LEFT, timestamp++);
288
289
290
291
292
    QTRY_COMPARE(releaseSpy.count(), 1);
}

void InternalWindowTest::testPointerAxis()
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
293
    QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded);
294
295
296
297
298
299
    QVERIFY(clientAddedSpy.isValid());
    HelperWindow win;
    win.setGeometry(0, 0, 100, 100);
    win.show();
    QSignalSpy wheelSpy(&win, &HelperWindow::wheel);
    QVERIFY(wheelSpy.isValid());
300
    QTRY_COMPARE(clientAddedSpy.count(), 1);
301
302

    quint32 timestamp = 1;
303
    kwinApp()->platform()->pointerMotion(QPoint(50, 50), timestamp++);
304

305
    kwinApp()->platform()->pointerAxisVertical(5.0, timestamp++);
306
    QTRY_COMPARE(wheelSpy.count(), 1);
307
    kwinApp()->platform()->pointerAxisHorizontal(5.0, timestamp++);
308
309
310
    QTRY_COMPARE(wheelSpy.count(), 2);
}

311
312
313
314
315
316
317
318
void InternalWindowTest::testKeyboard_data()
{
    QTest::addColumn<QPoint>("cursorPos");

    QTest::newRow("on Window") << QPoint(50, 50);
    QTest::newRow("outside Window") << QPoint(250, 250);
}

319
320
void InternalWindowTest::testKeyboard()
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
321
    QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded);
322
323
324
325
326
327
328
329
    QVERIFY(clientAddedSpy.isValid());
    HelperWindow win;
    win.setGeometry(0, 0, 100, 100);
    win.show();
    QSignalSpy pressSpy(&win, &HelperWindow::keyPressed);
    QVERIFY(pressSpy.isValid());
    QSignalSpy releaseSpy(&win, &HelperWindow::keyReleased);
    QVERIFY(releaseSpy.isValid());
330
    QTRY_COMPARE(clientAddedSpy.count(), 1);
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
331
    auto internalClient = clientAddedSpy.first().first().value<InternalClient *>();
332
333
    QVERIFY(internalClient);
    QVERIFY(internalClient->isInternal());
334
    QVERIFY(internalClient->readyForPainting());
335
336

    quint32 timestamp = 1;
337
    QFETCH(QPoint, cursorPos);
338
    kwinApp()->platform()->pointerMotion(cursorPos, timestamp++);
339

340
    kwinApp()->platform()->keyboardKeyPressed(KEY_A, timestamp++);
341
342
    QTRY_COMPARE(pressSpy.count(), 1);
    QCOMPARE(releaseSpy.count(), 0);
343
    kwinApp()->platform()->keyboardKeyReleased(KEY_A, timestamp++);
344
345
    QTRY_COMPARE(releaseSpy.count(), 1);
    QCOMPARE(pressSpy.count(), 1);
346
}
347

348
349
void InternalWindowTest::testKeyboardShowWithoutActivating()
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
350
    QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded);
351
352
353
354
355
356
357
358
359
    QVERIFY(clientAddedSpy.isValid());
    HelperWindow win;
    win.setProperty("_q_showWithoutActivating", true);
    win.setGeometry(0, 0, 100, 100);
    win.show();
    QSignalSpy pressSpy(&win, &HelperWindow::keyPressed);
    QVERIFY(pressSpy.isValid());
    QSignalSpy releaseSpy(&win, &HelperWindow::keyReleased);
    QVERIFY(releaseSpy.isValid());
360
    QTRY_COMPARE(clientAddedSpy.count(), 1);
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
361
    auto internalClient = clientAddedSpy.first().first().value<InternalClient *>();
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
    QVERIFY(internalClient);
    QVERIFY(internalClient->isInternal());
    QVERIFY(internalClient->readyForPainting());

    quint32 timestamp = 1;
    const QPoint cursorPos = QPoint(50, 50);
    kwinApp()->platform()->pointerMotion(cursorPos, timestamp++);

    kwinApp()->platform()->keyboardKeyPressed(KEY_A, timestamp++);
    QCOMPARE(pressSpy.count(), 0);
    QVERIFY(!pressSpy.wait(100));
    QCOMPARE(releaseSpy.count(), 0);
    kwinApp()->platform()->keyboardKeyReleased(KEY_A, timestamp++);
    QCOMPARE(releaseSpy.count(), 0);
    QVERIFY(!releaseSpy.wait(100));
    QCOMPARE(pressSpy.count(), 0);
}

380
381
382
383
void InternalWindowTest::testKeyboardTriggersLeave()
{
    // this test verifies that a leave event is sent to a client when an internal window
    // gets a key event
384
385
386
387
388
389
390
391
    QScopedPointer<Keyboard> keyboard(Test::waylandSeat()->createKeyboard());
    QVERIFY(!keyboard.isNull());
    QVERIFY(keyboard->isValid());
    QSignalSpy enteredSpy(keyboard.data(), &Keyboard::entered);
    QVERIFY(enteredSpy.isValid());
    QSignalSpy leftSpy(keyboard.data(), &Keyboard::left);
    QVERIFY(leftSpy.isValid());
    QScopedPointer<Surface> surface(Test::createSurface());
392
    QScopedPointer<XdgShellSurface> shellSurface(Test::createXdgShellStableSurface(surface.data()));
393
394
395
396
397

    // now let's render
    auto c = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue);
    QVERIFY(c);
    QVERIFY(c->isActive());
398
    QVERIFY(!c->isInternal());
399
400
401
402
403
404

    if (enteredSpy.isEmpty()) {
        QVERIFY(enteredSpy.wait());
    }
    QCOMPARE(enteredSpy.count(), 1);

405
    // create internal window
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
406
    QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded);
407
408
409
    QVERIFY(clientAddedSpy.isValid());
    HelperWindow win;
    win.setGeometry(0, 0, 100, 100);
410
    win.show();
411
412
413
414
    QSignalSpy pressSpy(&win, &HelperWindow::keyPressed);
    QVERIFY(pressSpy.isValid());
    QSignalSpy releaseSpy(&win, &HelperWindow::keyReleased);
    QVERIFY(releaseSpy.isValid());
415
    QTRY_COMPARE(clientAddedSpy.count(), 1);
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
416
    auto internalClient = clientAddedSpy.first().first().value<InternalClient *>();
417
418
419
420
    QVERIFY(internalClient);
    QVERIFY(internalClient->isInternal());
    QVERIFY(internalClient->readyForPainting());

421
422
423
424
    QVERIFY(leftSpy.isEmpty());
    QVERIFY(!leftSpy.wait(100));

    // now let's trigger a key, which should result in a leave
425
    quint32 timestamp = 1;
426
427
    kwinApp()->platform()->keyboardKeyPressed(KEY_A, timestamp++);
    QVERIFY(leftSpy.wait());
428
    QCOMPARE(pressSpy.count(), 1);
429
430

    kwinApp()->platform()->keyboardKeyReleased(KEY_A, timestamp++);
431
    QTRY_COMPARE(releaseSpy.count(), 1);
432
433
434
435
436
437

    // after hiding the internal window, next key press should trigger an enter
    win.hide();
    kwinApp()->platform()->keyboardKeyPressed(KEY_A, timestamp++);
    QVERIFY(enteredSpy.wait());
    kwinApp()->platform()->keyboardKeyReleased(KEY_A, timestamp++);
438
439
440
441

    // Destroy the test client.
    shellSurface.reset();
    QVERIFY(Test::waitForWindowDestroyed(c));
442
443
}

444
445
446
void InternalWindowTest::testTouch()
{
    // touch events for internal windows are emulated through mouse events
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
447
    QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded);
448
449
450
451
    QVERIFY(clientAddedSpy.isValid());
    HelperWindow win;
    win.setGeometry(0, 0, 100, 100);
    win.show();
452
    QTRY_COMPARE(clientAddedSpy.count(), 1);
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508

    QSignalSpy pressSpy(&win, &HelperWindow::mousePressed);
    QVERIFY(pressSpy.isValid());
    QSignalSpy releaseSpy(&win, &HelperWindow::mouseReleased);
    QVERIFY(releaseSpy.isValid());
    QSignalSpy moveSpy(&win, &HelperWindow::mouseMoved);
    QVERIFY(moveSpy.isValid());

    quint32 timestamp = 1;
    QCOMPARE(win.pressedButtons(), Qt::MouseButtons());
    kwinApp()->platform()->touchDown(0, QPointF(50, 50), timestamp++);
    QCOMPARE(pressSpy.count(), 1);
    QCOMPARE(win.latestGlobalMousePos(), QPoint(50, 50));
    QCOMPARE(win.pressedButtons(), Qt::MouseButtons(Qt::LeftButton));

    // further touch down should not trigger
    kwinApp()->platform()->touchDown(1, QPointF(75, 75), timestamp++);
    QCOMPARE(pressSpy.count(), 1);
    kwinApp()->platform()->touchUp(1, timestamp++);
    QCOMPARE(releaseSpy.count(), 0);
    QCOMPARE(win.latestGlobalMousePos(), QPoint(50, 50));
    QCOMPARE(win.pressedButtons(), Qt::MouseButtons(Qt::LeftButton));

    // another press
    kwinApp()->platform()->touchDown(1, QPointF(10, 10), timestamp++);
    QCOMPARE(pressSpy.count(), 1);
    QCOMPARE(win.latestGlobalMousePos(), QPoint(50, 50));
    QCOMPARE(win.pressedButtons(), Qt::MouseButtons(Qt::LeftButton));

    // simulate the move
    QCOMPARE(moveSpy.count(), 0);
    kwinApp()->platform()->touchMotion(0, QPointF(80, 90), timestamp++);
    QCOMPARE(moveSpy.count(), 1);
    QCOMPARE(win.latestGlobalMousePos(), QPoint(80, 90));
    QCOMPARE(win.pressedButtons(), Qt::MouseButtons(Qt::LeftButton));

    // move on other ID should not do anything
    kwinApp()->platform()->touchMotion(1, QPointF(20, 30), timestamp++);
    QCOMPARE(moveSpy.count(), 1);
    QCOMPARE(win.latestGlobalMousePos(), QPoint(80, 90));
    QCOMPARE(win.pressedButtons(), Qt::MouseButtons(Qt::LeftButton));

    // now up our main point
    kwinApp()->platform()->touchUp(0, timestamp++);
    QCOMPARE(releaseSpy.count(), 1);
    QCOMPARE(win.latestGlobalMousePos(), QPoint(80, 90));
    QCOMPARE(win.pressedButtons(), Qt::MouseButtons());

    // and up the additional point
    kwinApp()->platform()->touchUp(1, timestamp++);
    QCOMPARE(releaseSpy.count(), 1);
    QCOMPARE(moveSpy.count(), 1);
    QCOMPARE(win.latestGlobalMousePos(), QPoint(80, 90));
    QCOMPARE(win.pressedButtons(), Qt::MouseButtons());
}

509
510
void InternalWindowTest::testOpacity()
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
511
512
    // this test verifies that opacity is properly synced from QWindow to InternalClient
    QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded);
513
514
515
516
517
    QVERIFY(clientAddedSpy.isValid());
    HelperWindow win;
    win.setOpacity(0.5);
    win.setGeometry(0, 0, 100, 100);
    win.show();
518
    QTRY_COMPARE(clientAddedSpy.count(), 1);
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
519
    auto internalClient = clientAddedSpy.first().first().value<InternalClient *>();
520
521
522
523
    QVERIFY(internalClient);
    QVERIFY(internalClient->isInternal());
    QCOMPARE(internalClient->opacity(), 0.5);

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
524
    QSignalSpy opacityChangedSpy(internalClient, &InternalClient::opacityChanged);
525
526
527
528
529
530
    QVERIFY(opacityChangedSpy.isValid());
    win.setOpacity(0.75);
    QCOMPARE(opacityChangedSpy.count(), 1);
    QCOMPARE(internalClient->opacity(), 0.75);
}

531
532
void InternalWindowTest::testMove()
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
533
    QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded);
534
535
536
537
538
    QVERIFY(clientAddedSpy.isValid());
    HelperWindow win;
    win.setOpacity(0.5);
    win.setGeometry(0, 0, 100, 100);
    win.show();
539
    QTRY_COMPARE(clientAddedSpy.count(), 1);
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
540
    auto internalClient = clientAddedSpy.first().first().value<InternalClient *>();
541
542
543
544
545
546
    QVERIFY(internalClient);
    QCOMPARE(internalClient->geometry(), QRect(0, 0, 100, 100));

    // normal move should be synced
    internalClient->move(5, 10);
    QCOMPARE(internalClient->geometry(), QRect(5, 10, 100, 100));
547
    QTRY_COMPARE(win.geometry(), QRect(5, 10, 100, 100));
548
549
550
    // another move should also be synced
    internalClient->move(10, 20);
    QCOMPARE(internalClient->geometry(), QRect(10, 20, 100, 100));
551
    QTRY_COMPARE(win.geometry(), QRect(10, 20, 100, 100));
552
553
554
555
556
557
558
559
560

    // now move with a Geometry update blocker
    {
        GeometryUpdatesBlocker blocker(internalClient);
        internalClient->move(5, 10);
        // not synced!
        QCOMPARE(win.geometry(), QRect(10, 20, 100, 100));
    }
    // after destroying the blocker it should be synced
561
    QTRY_COMPARE(win.geometry(), QRect(5, 10, 100, 100));
562
563
}

564
565
566
567
568
569
570
571
572
573
void InternalWindowTest::testSkipCloseAnimation_data()
{
    QTest::addColumn<bool>("initial");

    QTest::newRow("set") << true;
    QTest::newRow("not set") << false;
}

void InternalWindowTest::testSkipCloseAnimation()
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
574
    QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded);
575
576
577
578
579
580
581
    QVERIFY(clientAddedSpy.isValid());
    HelperWindow win;
    win.setOpacity(0.5);
    win.setGeometry(0, 0, 100, 100);
    QFETCH(bool, initial);
    win.setProperty("KWIN_SKIP_CLOSE_ANIMATION", initial);
    win.show();
582
    QTRY_COMPARE(clientAddedSpy.count(), 1);
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
583
    auto internalClient = clientAddedSpy.first().first().value<InternalClient *>();
584
585
586
587
588
589
590
591
592
593
594
595
    QVERIFY(internalClient);
    QCOMPARE(internalClient->skipsCloseAnimation(), initial);
    QSignalSpy skipCloseChangedSpy(internalClient, &Toplevel::skipCloseAnimationChanged);
    QVERIFY(skipCloseChangedSpy.isValid());
    win.setProperty("KWIN_SKIP_CLOSE_ANIMATION", !initial);
    QCOMPARE(skipCloseChangedSpy.count(), 1);
    QCOMPARE(internalClient->skipsCloseAnimation(), !initial);
    win.setProperty("KWIN_SKIP_CLOSE_ANIMATION", initial);
    QCOMPARE(skipCloseChangedSpy.count(), 2);
    QCOMPARE(internalClient->skipsCloseAnimation(), initial);
}

596
597
void InternalWindowTest::testModifierClickUnrestrictedMove()
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
598
    QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded);
599
600
601
602
603
    QVERIFY(clientAddedSpy.isValid());
    HelperWindow win;
    win.setGeometry(0, 0, 100, 100);
    win.setFlags(win.flags() & ~Qt::FramelessWindowHint);
    win.show();
604
    QTRY_COMPARE(clientAddedSpy.count(), 1);
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
605
    auto internalClient = clientAddedSpy.first().first().value<InternalClient *>();
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
    QVERIFY(internalClient);
    QVERIFY(internalClient->isDecorated());

    KConfigGroup group = kwinApp()->config()->group("MouseBindings");
    group.writeEntry("CommandAllKey", "Alt");
    group.writeEntry("CommandAll1", "Move");
    group.writeEntry("CommandAll2", "Move");
    group.writeEntry("CommandAll3", "Move");
    group.sync();
    workspace()->slotReconfigure();
    QCOMPARE(options->commandAllModifier(), Qt::AltModifier);
    QCOMPARE(options->commandAll1(), Options::MouseUnrestrictedMove);
    QCOMPARE(options->commandAll2(), Options::MouseUnrestrictedMove);
    QCOMPARE(options->commandAll3(), Options::MouseUnrestrictedMove);

    // move cursor on window
    Cursor::setPos(internalClient->geometry().center());

    // simulate modifier+click
    quint32 timestamp = 1;
    kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTALT, timestamp++);
    QVERIFY(!internalClient->isMove());
    kwinApp()->platform()->pointerButtonPressed(BTN_LEFT, timestamp++);
    QVERIFY(internalClient->isMove());
    // release modifier should not change it
    kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTALT, timestamp++);
    QVERIFY(internalClient->isMove());
    // but releasing the key should end move/resize
    kwinApp()->platform()->pointerButtonReleased(BTN_LEFT, timestamp++);
    QVERIFY(!internalClient->isMove());
}

void InternalWindowTest::testModifierScroll()
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
640
    QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded);
641
642
643
644
645
    QVERIFY(clientAddedSpy.isValid());
    HelperWindow win;
    win.setGeometry(0, 0, 100, 100);
    win.setFlags(win.flags() & ~Qt::FramelessWindowHint);
    win.show();
646
    QTRY_COMPARE(clientAddedSpy.count(), 1);
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
647
    auto internalClient = clientAddedSpy.first().first().value<InternalClient *>();
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
    QVERIFY(internalClient);
    QVERIFY(internalClient->isDecorated());

    KConfigGroup group = kwinApp()->config()->group("MouseBindings");
    group.writeEntry("CommandAllKey", "Alt");
    group.writeEntry("CommandAllWheel", "change opacity");
    group.sync();
    workspace()->slotReconfigure();

    // move cursor on window
    Cursor::setPos(internalClient->geometry().center());

    // set the opacity to 0.5
    internalClient->setOpacity(0.5);
    QCOMPARE(internalClient->opacity(), 0.5);
    quint32 timestamp = 1;
    kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTALT, timestamp++);
    kwinApp()->platform()->pointerAxisVertical(-5, timestamp++);
    QCOMPARE(internalClient->opacity(), 0.6);
    kwinApp()->platform()->pointerAxisVertical(5, timestamp++);
    QCOMPARE(internalClient->opacity(), 0.5);
    kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTALT, timestamp++);
}

672
673
void InternalWindowTest::testPopup()
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
674
    QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded);
675
676
677
678
679
    QVERIFY(clientAddedSpy.isValid());
    HelperWindow win;
    win.setGeometry(0, 0, 100, 100);
    win.setFlags(win.flags() | Qt::Popup);
    win.show();
680
    QTRY_COMPARE(clientAddedSpy.count(), 1);
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
681
    auto internalClient = clientAddedSpy.first().first().value<InternalClient *>();
682
683
684
685
    QVERIFY(internalClient);
    QCOMPARE(internalClient->isPopupWindow(), true);
}

686
687
688
689
690
691
692
void InternalWindowTest::testScale()
{
    QMetaObject::invokeMethod(kwinApp()->platform(), "setVirtualOutputs", Qt::DirectConnection,
        Q_ARG(int, 2),
        Q_ARG(QVector<QRect>, QVector<QRect>({QRect(0,0,1280, 1024), QRect(1280/2, 0, 1280, 1024)})),
        Q_ARG(QVector<int>, QVector<int>({2,2})));

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
693
    QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded);
694
695
696
697
698
699
    QVERIFY(clientAddedSpy.isValid());
    HelperWindow win;
    win.setGeometry(0, 0, 100, 100);
    win.setFlags(win.flags() | Qt::Popup);
    win.show();
    QCOMPARE(win.devicePixelRatio(), 2.0);
700
    QTRY_COMPARE(clientAddedSpy.count(), 1);
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
701
702
    auto internalClient = clientAddedSpy.first().first().value<InternalClient *>();
    QCOMPARE(internalClient->bufferScale(), 2);
703
704
}

705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
void InternalWindowTest::testWindowType_data()
{
    QTest::addColumn<NET::WindowType>("windowType");

    QTest::newRow("normal") << NET::Normal;
    QTest::newRow("desktop") << NET::Desktop;
    QTest::newRow("Dock") << NET::Dock;
    QTest::newRow("Toolbar") << NET::Toolbar;
    QTest::newRow("Menu") << NET::Menu;
    QTest::newRow("Dialog") << NET::Dialog;
    QTest::newRow("Utility") << NET::Utility;
    QTest::newRow("Splash") << NET::Splash;
    QTest::newRow("DropdownMenu") << NET::DropdownMenu;
    QTest::newRow("PopupMenu") << NET::PopupMenu;
    QTest::newRow("Tooltip") << NET::Tooltip;
    QTest::newRow("Notification") << NET::Notification;
    QTest::newRow("ComboBox") << NET::ComboBox;
    QTest::newRow("OnScreenDisplay") << NET::OnScreenDisplay;
723
    QTest::newRow("CriticalNotification") << NET::CriticalNotification;
724
725
726
727
}

void InternalWindowTest::testWindowType()
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
728
    QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded);
729
730
731
732
733
734
735
    QVERIFY(clientAddedSpy.isValid());
    HelperWindow win;
    win.setGeometry(0, 0, 100, 100);
    QFETCH(NET::WindowType, windowType);
    KWindowSystem::setType(win.winId(), windowType);
    win.show();
    QTRY_COMPARE(clientAddedSpy.count(), 1);
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
736
    auto internalClient = clientAddedSpy.first().first().value<InternalClient *>();
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
    QVERIFY(internalClient);
    QCOMPARE(internalClient->windowType(), windowType);
}

void InternalWindowTest::testChangeWindowType_data()
{
    QTest::addColumn<NET::WindowType>("windowType");

    QTest::newRow("desktop") << NET::Desktop;
    QTest::newRow("Dock") << NET::Dock;
    QTest::newRow("Toolbar") << NET::Toolbar;
    QTest::newRow("Menu") << NET::Menu;
    QTest::newRow("Dialog") << NET::Dialog;
    QTest::newRow("Utility") << NET::Utility;
    QTest::newRow("Splash") << NET::Splash;
    QTest::newRow("DropdownMenu") << NET::DropdownMenu;
    QTest::newRow("PopupMenu") << NET::PopupMenu;
    QTest::newRow("Tooltip") << NET::Tooltip;
    QTest::newRow("Notification") << NET::Notification;
    QTest::newRow("ComboBox") << NET::ComboBox;
    QTest::newRow("OnScreenDisplay") << NET::OnScreenDisplay;
758
    QTest::newRow("CriticalNotification") << NET::CriticalNotification;
759
760
761
762
}

void InternalWindowTest::testChangeWindowType()
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
763
    QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded);
764
765
766
767
768
    QVERIFY(clientAddedSpy.isValid());
    HelperWindow win;
    win.setGeometry(0, 0, 100, 100);
    win.show();
    QTRY_COMPARE(clientAddedSpy.count(), 1);
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
769
    auto internalClient = clientAddedSpy.first().first().value<InternalClient *>();
770
771
772
773
774
775
776
777
778
779
780
781
782
    QVERIFY(internalClient);
    QCOMPARE(internalClient->windowType(), NET::Normal);

    QFETCH(NET::WindowType, windowType);
    KWindowSystem::setType(win.winId(), windowType);
    QTRY_COMPARE(internalClient->windowType(), windowType);

    KWindowSystem::setType(win.winId(), NET::Normal);
    QTRY_COMPARE(internalClient->windowType(), NET::Normal);
}

void InternalWindowTest::testEffectWindow()
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
783
    QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded);
784
785
786
787
    QVERIFY(clientAddedSpy.isValid());
    HelperWindow win;
    win.setGeometry(0, 0, 100, 100);
    win.show();
788
    QTRY_COMPARE(clientAddedSpy.count(), 1);
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
789
    auto internalClient = clientAddedSpy.first().first().value<InternalClient *>();
790
791
792
793
794
795
796
    QVERIFY(internalClient);
    QVERIFY(internalClient->effectWindow());
    QCOMPARE(internalClient->effectWindow()->internalWindow(), &win);

    QCOMPARE(effects->findWindow(&win), internalClient->effectWindow());
    QCOMPARE(effects->findWindow(&win)->internalWindow(), &win);
}
797

798
799
}

Martin Flöser's avatar
Martin Flöser committed
800
WAYLANDTEST_MAIN(KWin::InternalWindowTest)
801
#include "internal_window.moc"