toplevel.cpp 20.2 KB
Newer Older
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
1
2
3
/*
    KWin - the KDE window manager
    This file is part of the KDE project.
4

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
5
    SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
6

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
7
8
    SPDX-License-Identifier: GPL-2.0-or-later
*/
9
10
#include "toplevel.h"

11
#include "abstract_client.h"
12
13
14
#ifdef KWIN_BUILD_ACTIVITIES
#include "activities.h"
#endif
15
#include "atoms.h"
16
#include "client_machine.h"
17
#include "composite.h"
18
#include "effects.h"
19
#include "screens.h"
20
#include "shadow.h"
21
#include "workspace.h"
22

23
#include <KWaylandServer/surface_interface.h>
24

Martin Flöser's avatar
Martin Flöser committed
25
26
#include <QDebug>

27
28
29
namespace KWin
{

30
Toplevel::Toplevel()
31
    : m_visual(XCB_NONE)
32
    , bit_depth(24)
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
33
    , info(nullptr)
34
    , ready_for_painting(false)
Laurent Montel's avatar
Laurent Montel committed
35
    , m_isDamaged(false)
36
    , m_internalId(QUuid::createUuid())
37
    , m_client()
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
38
    , damage_handle(XCB_NONE)
39
    , is_shape(false)
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
40
    , effect_window(nullptr)
41
    , m_clientMachine(new ClientMachine(this))
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
42
    , m_wmClientLeader(XCB_WINDOW_NONE)
43
    , m_damageReplyPending(false)
44
    , m_screen(0)
45
    , m_skipCloseAnimation(false)
46
{
47
    connect(this, &Toplevel::damaged, this, &Toplevel::needsRepaint);
48
49
    connect(screens(), &Screens::changed, this, &Toplevel::checkScreen);
    connect(screens(), &Screens::countChanged, this, &Toplevel::checkScreen);
50
    setupCheckScreenConnection();
51
    connect(this, &Toplevel::bufferGeometryChanged, this, &Toplevel::inputTransformationChanged);
52
53
54

    // Only for compatibility reasons, drop in the next major release.
    connect(this, &Toplevel::frameGeometryChanged, this, &Toplevel::geometryChanged);
55
}
56
57

Toplevel::~Toplevel()
58
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
59
    Q_ASSERT(damage_handle == XCB_NONE);
60
    delete info;
61
}
62

63
QDebug operator<<(QDebug debug, const Toplevel *toplevel)
64
{
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
    QDebugStateSaver saver(debug);
    debug.nospace();
    if (toplevel) {
        debug << toplevel->metaObject()->className() << '(' << static_cast<const void *>(toplevel);
        debug << ", windowId=0x" << Qt::hex << toplevel->windowId() << Qt::dec;
        if (const KWaylandServer::SurfaceInterface *surface = toplevel->surface()) {
            debug << ", surface=" << surface;
        }
        const AbstractClient *client = qobject_cast<const AbstractClient *>(toplevel);
        if (client) {
            if (!client->isPopupWindow()) {
                debug << ", caption=" << client->caption();
            }
            if (client->transientFor()) {
                debug << ", transientFor=" << client->transientFor();
            }
        }
        if (debug.verbosity() > 2) {
            debug << ", frameGeometry=" << toplevel->frameGeometry();
            debug << ", resourceName=" << toplevel->resourceName();
            debug << ", resourceClass=" << toplevel->resourceClass();
        }
        debug << ')';
    } else {
        debug << "Toplevel(0x0)";
    }
    return debug;
92
}
93

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
94
void Toplevel::detectShape(xcb_window_t id)
95
{
96
    const bool wasShape = is_shape;
97
    is_shape = Xcb::Extensions::self()->hasShape(id);
98
99
100
    if (wasShape != is_shape) {
        emit shapedChanged();
    }
101
}
102
103

// used only by Deleted::copy()
104
105
void Toplevel::copyToDeleted(Toplevel* c)
{
106
    m_internalId = c->internalId();
107
    m_frameGeometry = c->m_frameGeometry;
108
    m_clientGeometry = c->m_clientGeometry;
109
    m_visual = c->m_visual;
110
111
    bit_depth = c->bit_depth;
    info = c->info;
112
    m_client.reset(c->m_client, false);
113
    ready_for_painting = c->ready_for_painting;
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
114
    damage_handle = XCB_NONE;
115
116
117
    damage_region = c->damage_region;
    is_shape = c->is_shape;
    effect_window = c->effect_window;
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
118
    if (effect_window != nullptr)
119
        effect_window->setWindow(this);
120
121
    resource_name = c->resourceName();
    resource_class = c->resourceClass();
122
123
    m_clientMachine = c->m_clientMachine;
    m_clientMachine->setParent(this);
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
124
    m_wmClientLeader = c->wmClientLeader();
125
    opaque_region = c->opaqueRegion();
126
    m_screen = c->m_screen;
127
    m_skipCloseAnimation = c->m_skipCloseAnimation;
128
    m_internalFBO = c->m_internalFBO;
129
    m_internalImage = c->m_internalImage;
130
}
131
132
133
134

// before being deleted, remove references to everything that's now
// owner by Deleted
void Toplevel::disownDataPassedToDeleted()
135
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
136
    info = nullptr;
137
}
138

139
QRect Toplevel::visibleRect() const
140
{
141
142
143
    // There's no strict order between frame geometry and buffer geometry.
    QRect rect = frameGeometry() | bufferGeometry();

144
    if (shadow() && !shadow()->shadowRegion().isEmpty()) {
145
        rect |= shadow()->shadowRegion().boundingRect().translated(pos());
146
    }
147
148

    return rect;
149
}
150

151
152
153
154
155
156
Xcb::Property Toplevel::fetchWmClientLeader() const
{
    return Xcb::Property(false, window(), atoms->wm_client_leader, XCB_ATOM_WINDOW, 0, 10000);
}

void Toplevel::readWmClientLeader(Xcb::Property &prop)
157
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
158
    m_wmClientLeader = prop.value<xcb_window_t>(window());
159
}
160

161
162
163
164
165
166
void Toplevel::getWmClientLeader()
{
    auto prop = fetchWmClientLeader();
    readWmClientLeader(prop);
}

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
167
168
169
/**
 * Returns sessionId for this client,
 * taken either from its window or from the leader window.
170
 */
171
QByteArray Toplevel::sessionId() const
172
{
173
    QByteArray result = Xcb::StringProperty(window(), atoms->sm_client_id);
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
174
175
176
    if (result.isEmpty() && m_wmClientLeader && m_wmClientLeader != window()) {
        result = Xcb::StringProperty(m_wmClientLeader, atoms->sm_client_id);
    }
177
    return result;
178
}
179

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
180
181
182
/**
 * Returns command property for this client,
 * taken either from its window or from the leader window.
183
 */
184
185
186
QByteArray Toplevel::wmCommand()
{
    QByteArray result = Xcb::StringProperty(window(), XCB_ATOM_WM_COMMAND);
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
187
188
189
    if (result.isEmpty() && m_wmClientLeader && m_wmClientLeader != window()) {
        result = Xcb::StringProperty(m_wmClientLeader, XCB_ATOM_WM_COMMAND);
    }
190
191
192
193
    result.replace(0, ' ');
    return result;
}

194
void Toplevel::getWmClientMachine()
195
{
196
    m_clientMachine->resolve(window(), wmClientLeader());
197
}
198

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
199
200
201
/**
 * Returns client machine for this client,
 * taken either from its window or from the leader window.
202
 */
203
204
QByteArray Toplevel::wmClientMachine(bool use_localhost) const
{
205
206
207
208
209
    if (!m_clientMachine) {
        // this should never happen
        return QByteArray();
    }
    if (use_localhost && m_clientMachine->isLocal()) {
210
        // special name for the local machine (localhost)
211
        return ClientMachine::localhost();
212
    }
213
    return m_clientMachine->hostName();
214
}
215

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
216
217
218
/**
 * Returns client leader window for this client.
 * Returns the client window itself if no leader window is defined.
219
 */
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
220
xcb_window_t Toplevel::wmClientLeader() const
221
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
222
223
224
    if (m_wmClientLeader != XCB_WINDOW_NONE) {
        return m_wmClientLeader;
    }
225
    return window();
226
}
227
228

void Toplevel::getResourceClass()
229
{
230
231
232
    if (!info) {
        return;
    }
233
234
235
236
237
238
239
    setResourceClass(QByteArray(info->windowClassName()).toLower(), QByteArray(info->windowClassClass()).toLower());
}

void Toplevel::setResourceClass(const QByteArray &name, const QByteArray &className)
{
    resource_name  = name;
    resource_class = className;
240
    emit windowClassChanged();
241
}
242

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
243
244
245
246
247
bool Toplevel::resourceMatch(const Toplevel *c1, const Toplevel *c2)
{
    return c1->resourceClass() == c2->resourceClass();
}

248
double Toplevel::opacity() const
249
{
250
251
252
    if (!info) {
        return 1.0;
    }
253
    if (info->opacity() == 0xffffffff)
254
255
        return 1.0;
    return info->opacity() * 1.0 / 0xffffffff;
256
}
257

258
259
void Toplevel::setOpacity(double new_opacity)
{
260
261
262
263
    if (!info) {
        return;
    }

264
    double old_opacity = opacity();
265
266
    new_opacity = qBound(0.0, new_opacity, 1.0);
    if (old_opacity == new_opacity)
267
        return;
268
269
    info->setOpacity(static_cast< unsigned long >(new_opacity * 0xffffffff));
    if (compositing()) {
270
        addRepaintFull();
271
        emit opacityChanged(this, old_opacity);
272
    }
273
}
274

275
276
277
278
279
280
281
282
bool Toplevel::setupCompositing()
{
    if (!compositing())
        return false;

    if (damage_handle != XCB_NONE)
        return false;

283
    if (kwinApp()->operationMode() == Application::OperationModeX11 && !surface()) {
284
        damage_handle = xcb_generate_id(connection());
285
        xcb_damage_create(connection(), damage_handle, frameId(), XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY);
286
287
    }

288
    damage_region = QRegion(0, 0, width(), height());
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
    effect_window = new EffectWindowImpl(this);

    Compositor::self()->scene()->addToplevel(this);

    return true;
}

void Toplevel::finishCompositing(ReleaseReason releaseReason)
{
    if (kwinApp()->operationMode() == Application::OperationModeX11 && damage_handle == XCB_NONE)
        return;
    if (effect_window->window() == this) { // otherwise it's already passed to Deleted, don't free data
        discardWindowPixmap();
        delete effect_window;
    }

    if (damage_handle != XCB_NONE &&
            releaseReason != ReleaseReason::Destroyed) {
        xcb_damage_destroy(connection(), damage_handle);
    }

    damage_handle = XCB_NONE;
    damage_region = QRegion();
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
312
    effect_window = nullptr;
313
314
315
316
317
}

void Toplevel::discardWindowPixmap()
{
    addDamageFull();
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
318
    if (effectWindow() != nullptr && effectWindow()->sceneWindow() != nullptr)
319
        effectWindow()->sceneWindow()->discardPixmap();
320
321
322
323
324
325
}

void Toplevel::damageNotifyEvent()
{
    m_isDamaged = true;

326
    // Note: The damage is supposed to specify the damage extents,
327
328
    //       but we don't know it at this point. No one who connects
    //       to this signal uses the rect however.
329
    emit damaged(this, {});
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
}

bool Toplevel::compositing() const
{
    if (!Workspace::self()) {
        return false;
    }
    return Workspace::self()->compositing();
}

bool Toplevel::resetAndFetchDamage()
{
    if (!m_isDamaged)
        return false;

    if (damage_handle == XCB_NONE) {
        m_isDamaged = false;
        return true;
    }

    xcb_connection_t *conn = connection();

    // Create a new region and copy the damage region to it,
    // resetting the damaged state.
    xcb_xfixes_region_t region = xcb_generate_id(conn);
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
355
    xcb_xfixes_create_region(conn, region, 0, nullptr);
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
    xcb_damage_subtract(conn, damage_handle, 0, region);

    // Send a fetch-region request and destroy the region
    m_regionCookie = xcb_xfixes_fetch_region_unchecked(conn, region);
    xcb_xfixes_destroy_region(conn, region);

    m_isDamaged = false;
    m_damageReplyPending = true;

    return m_damageReplyPending;
}

void Toplevel::getDamageRegionReply()
{
    if (!m_damageReplyPending)
        return;

    m_damageReplyPending = false;

    // Get the fetch-region reply
    xcb_xfixes_fetch_region_reply_t *reply =
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
377
            xcb_xfixes_fetch_region_reply(connection(), m_regionCookie, nullptr);
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399

    if (!reply)
        return;

    // Convert the reply to a QRegion
    int count = xcb_xfixes_fetch_region_rectangles_length(reply);
    QRegion region;

    if (count > 1 && count < 16) {
        xcb_rectangle_t *rects = xcb_xfixes_fetch_region_rectangles(reply);

        QVector<QRect> qrects;
        qrects.reserve(count);

        for (int i = 0; i < count; i++)
            qrects << QRect(rects[i].x, rects[i].y, rects[i].width, rects[i].height);

        region.setRects(qrects.constData(), count);
    } else
        region += QRect(reply->extents.x, reply->extents.y,
                        reply->extents.width, reply->extents.height);

400
401
402
    const QRect bufferRect = bufferGeometry();
    const QRect frameRect = frameGeometry();

403
    damage_region += region;
404
    addRepaint(region.translated(bufferRect.topLeft() - frameRect.topLeft()));
405
406
407
408
409
410
411
412
413

    free(reply);
}

void Toplevel::addDamageFull()
{
    if (!compositing())
        return;

414
415
    const QRect bufferRect = bufferGeometry();
    const QRect frameRect = frameGeometry();
416

417
418
419
    const int offsetX = bufferRect.x() - frameRect.x();
    const int offsetY = bufferRect.y() - frameRect.y();

420
    const QRect damagedRect(0, 0, bufferRect.width(), bufferRect.height());
421
422

    damage_region = damagedRect;
423
    addRepaint(damagedRect.translated(offsetX, offsetY));
424

425
    emit damaged(this, damage_region);
426
427
428
429
430
431
432
}

void Toplevel::resetDamage()
{
    damage_region = QRegion();
}

433
void Toplevel::addRepaint(const QRect &rect)
434
{
435
    addRepaint(QRegion(rect));
436
437
}

438
void Toplevel::addRepaint(int x, int y, int width, int height)
439
{
440
    addRepaint(QRegion(x, y, width, height));
441
442
}

443
void Toplevel::addRepaint(const QRegion &region)
444
{
445
    if (!effectWindow() || !effectWindow()->sceneWindow()) {
446
447
        return;
    }
448
    effectWindow()->sceneWindow()->addRepaint(region);
449
450
451
    emit needsRepaint();
}

452
void Toplevel::addLayerRepaint(const QRect &rect)
453
{
454
    addLayerRepaint(QRegion(rect));
455
456
}

457
void Toplevel::addLayerRepaint(int x, int y, int width, int height)
458
{
459
    addLayerRepaint(QRegion(x, y, width, height));
460
461
}

462
void Toplevel::addLayerRepaint(const QRegion &region)
463
{
464
    if (!effectWindow() || !effectWindow()->sceneWindow()) {
465
        return;
466
467
    }
    effectWindow()->sceneWindow()->addLayerRepaint(region);
468
469
470
471
472
    emit needsRepaint();
}

void Toplevel::addRepaintFull()
{
473
    addRepaint(visibleRect().translated(-pos()));
474
475
476
477
478
479
480
481
482
483
484
485
486
487
}

void Toplevel::addWorkspaceRepaint(int x, int y, int w, int h)
{
    addWorkspaceRepaint(QRect(x, y, w, h));
}

void Toplevel::addWorkspaceRepaint(const QRect& r2)
{
    if (!compositing())
        return;
    Compositor::self()->addRepaint(r2);
}

488
489
490
491
492
493
494
void Toplevel::addWorkspaceRepaint(const QRegion &region)
{
    if (compositing()) {
        Compositor::self()->addRepaint(region);
    }
}

495
496
497
498
499
500
501
502
bool Toplevel::wantsRepaint() const
{
    if (!effectWindow() || !effectWindow()->sceneWindow()) {
        return false;
    }
    return effectWindow()->sceneWindow()->wantsRepaint();
}

503
504
505
506
507
508
509
510
511
512
513
void Toplevel::setReadyForPainting()
{
    if (!ready_for_painting) {
        ready_for_painting = true;
        if (compositing()) {
            addRepaintFull();
            emit windowShown(this);
        }
    }
}

514
void Toplevel::deleteEffectWindow()
515
{
516
    delete effect_window;
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
517
    effect_window = nullptr;
518
}
519

520
void Toplevel::checkScreen()
521
{
522
    if (screens()->count() == 1) {
523
524
525
526
        if (m_screen != 0) {
            m_screen = 0;
            emit screenChanged();
        }
527
    } else {
528
        const int s = screens()->number(frameGeometry().center());
529
530
531
532
        if (s != m_screen) {
            m_screen = s;
            emit screenChanged();
        }
533
    }
534
535
536
537
    qreal newScale = screens()->scale(m_screen);
    if (newScale != m_screenScale) {
        m_screenScale = newScale;
        emit screenScaleChanged();
538
539
540
541
542
    }
}

void Toplevel::setupCheckScreenConnection()
{
543
    connect(this, &Toplevel::frameGeometryChanged, this, &Toplevel::checkScreen);
544
545
546
547
548
    checkScreen();
}

void Toplevel::removeCheckScreenConnection()
{
549
    disconnect(this, &Toplevel::frameGeometryChanged, this, &Toplevel::checkScreen);
550
551
552
553
554
}

int Toplevel::screen() const
{
    return m_screen;
555
}
556

557
558
559
560
561
qreal Toplevel::screenScale() const
{
    return m_screenScale;
}

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
562
563
qreal Toplevel::bufferScale() const
{
564
    return surface() ? surface()->bufferScale() : 1;
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
565
566
}

567
568
bool Toplevel::isOnScreen(int screen) const
{
569
    return screens()->geometry(screen).intersects(frameGeometry());
570
571
572
573
574
}

bool Toplevel::isOnActiveScreen() const
{
    return isOnScreen(screens()->current());
575
}
576

577
void Toplevel::updateShadow()
578
{
Thomas Lübking's avatar
Thomas Lübking committed
579
    QRect dirtyRect;  // old & new shadow region
580
    const QRect oldVisibleRect = visibleRect();
581
    addWorkspaceRepaint(oldVisibleRect);
582
    if (shadow()) {
Thomas Lübking's avatar
Thomas Lübking committed
583
        dirtyRect = shadow()->shadowRegion().boundingRect();
584
585
586
587
        if (!effectWindow()->sceneWindow()->shadow()->updateShadow()) {
            effectWindow()->sceneWindow()->updateShadow(nullptr);
        }
        emit shadowChanged();
588
    } else {
589
        Shadow::createShadow(this);
Thomas Lübking's avatar
Thomas Lübking committed
590
    }
591
    if (shadow())
Thomas Lübking's avatar
Thomas Lübking committed
592
        dirtyRect |= shadow()->shadowRegion().boundingRect();
593
594
    if (oldVisibleRect != visibleRect())
        emit paddingChanged(this, oldVisibleRect);
Thomas Lübking's avatar
Thomas Lübking committed
595
596
    if (dirtyRect.isValid()) {
        dirtyRect.translate(pos());
597
        addLayerRepaint(dirtyRect);
598
599
600
    }
}

601
Shadow *Toplevel::shadow()
602
{
603
604
605
    if (effectWindow() && effectWindow()->sceneWindow()) {
        return effectWindow()->sceneWindow()->shadow();
    } else {
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
606
        return nullptr;
607
608
609
610
611
612
613
614
    }
}

const Shadow *Toplevel::shadow() const
{
    if (effectWindow() && effectWindow()->sceneWindow()) {
        return effectWindow()->sceneWindow()->shadow();
    } else {
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
615
        return nullptr;
616
    }
617
}
618

619
620
621
622
623
bool Toplevel::wantsShadowToBeRendered() const
{
    return true;
}

624
625
void Toplevel::getWmOpaqueRegion()
{
626
627
628
629
    if (!info) {
        return;
    }

Martin Flöser's avatar
Martin Flöser committed
630
    const auto rects = info->opaqueRegion();
631
    QRegion new_opaque_region;
Martin Flöser's avatar
Martin Flöser committed
632
633
634
    for (const auto &r : rects) {
        new_opaque_region += QRect(r.pos.x, r.pos.y, r.size.width, r.size.height);
    }
635
636
637
638

    opaque_region = new_opaque_region;
}

639
640
641
642
643
644
645
646
647
648
bool Toplevel::isClient() const
{
    return false;
}

bool Toplevel::isDeleted() const
{
    return false;
}

649
650
651
bool Toplevel::isOnCurrentActivity() const
{
#ifdef KWIN_BUILD_ACTIVITIES
652
653
654
    if (!Activities::self()) {
        return true;
    }
655
656
657
658
659
660
    return isOnActivity(Activities::self()->current());
#else
    return true;
#endif
}

661
662
663
664
665
666
void Toplevel::elevate(bool elevate)
{
    if (!effectWindow()) {
        return;
    }
    effectWindow()->elevate(elevate);
667
    addWorkspaceRepaint(visibleRect());
668
669
}

Martin Flöser's avatar
Martin Flöser committed
670
671
pid_t Toplevel::pid() const
{
672
673
674
    if (!info) {
        return -1;
    }
Martin Flöser's avatar
Martin Flöser committed
675
676
677
    return info->pid();
}

678
679
680
681
682
xcb_window_t Toplevel::frameId() const
{
    return m_client;
}

683
684
685
686
687
688
Xcb::Property Toplevel::fetchSkipCloseAnimation() const
{
    return Xcb::Property(false, window(), atoms->kde_skip_close_animation, XCB_ATOM_CARDINAL, 0, 1);
}

void Toplevel::readSkipCloseAnimation(Xcb::Property &property)
689
{
690
    setSkipCloseAnimation(property.toBool());
691
692
}

693
694
695
696
697
698
void Toplevel::getSkipCloseAnimation()
{
    Xcb::Property property = fetchSkipCloseAnimation();
    readSkipCloseAnimation(property);
}

699
700
701
702
703
704
705
706
707
708
709
710
711
712
bool Toplevel::skipsCloseAnimation() const
{
    return m_skipCloseAnimation;
}

void Toplevel::setSkipCloseAnimation(bool set)
{
    if (set == m_skipCloseAnimation) {
        return;
    }
    m_skipCloseAnimation = set;
    emit skipCloseAnimationChanged();
}

713
void Toplevel::setSurface(KWaylandServer::SurfaceInterface *surface)
714
715
716
717
{
    if (m_surface == surface) {
        return;
    }
718
    using namespace KWaylandServer;
719
720
    if (m_surface) {
        disconnect(m_surface, &SurfaceInterface::damaged, this, &Toplevel::addDamage);
721
        disconnect(m_surface, &SurfaceInterface::sizeChanged, this, &Toplevel::discardWindowPixmap);
722
723
724
    }
    m_surface = surface;
    connect(m_surface, &SurfaceInterface::damaged, this, &Toplevel::addDamage);
725
    connect(m_surface, &SurfaceInterface::sizeChanged, this, &Toplevel::discardWindowPixmap);
726
727
728
729
730
731
732
733
734
    connect(m_surface, &SurfaceInterface::subSurfaceTreeChanged, this,
        [this] {
            // TODO improve to only update actual visual area
            if (ready_for_painting) {
                addDamageFull();
                m_isDamaged = true;
            }
        }
    );
735
736
737
    connect(m_surface, &SurfaceInterface::destroyed, this,
        [this] {
            m_surface = nullptr;
Gang Wu's avatar
Gang Wu committed
738
            m_surfaceId = 0;
739
740
        }
    );
Gang Wu's avatar
Gang Wu committed
741
    m_surfaceId = surface->id();
742
    emit surfaceChanged();
743
744
745
746
747
748
}

void Toplevel::addDamage(const QRegion &damage)
{
    m_isDamaged = true;
    damage_region += damage;
749
    emit damaged(this, damage);
750
751
}

752
753
QByteArray Toplevel::windowRole() const
{
754
755
756
    if (!info) {
        return {};
    }
757
758
759
    return QByteArray(info->windowRole());
}

760
761
762
763
764
765
766
767
768
769
770
771
void Toplevel::setDepth(int depth)
{
    if (bit_depth == depth) {
        return;
    }
    const bool oldAlpha = hasAlpha();
    bit_depth = depth;
    if (oldAlpha != hasAlpha()) {
        emit hasAlphaChanged();
    }
}

772
773
774
775
QRegion Toplevel::inputShape() const
{
    if (m_surface) {
        return m_surface->input();
Martin Flöser's avatar
Martin Flöser committed
776
777
778
779
    } else {
        // TODO: maybe also for X11?
        return QRegion();
    }
780
781
}

782
783
784
785
786
787
788
QMatrix4x4 Toplevel::inputTransformation() const
{
    QMatrix4x4 m;
    m.translate(-x(), -y());
    return m;
}

789
790
791
792
793
794
795
796
bool Toplevel::hitTest(const QPoint &point) const
{
    if (m_surface && m_surface->isMapped()) {
        return m_surface->inputSurfaceAt(mapToLocal(point));
    }
    return inputGeometry().contains(point);
}

797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
QPoint Toplevel::mapToFrame(const QPoint &point) const
{
    return point - frameGeometry().topLeft();
}

QPoint Toplevel::mapToLocal(const QPoint &point) const
{
    return point - bufferGeometry().topLeft();
}

QPointF Toplevel::mapToLocal(const QPointF &point) const
{
    return point - bufferGeometry().topLeft();
}

812
813
814
815
816
quint32 Toplevel::windowId() const
{
    return window();
}

817
818
QRect Toplevel::inputGeometry() const
{
819
    return frameGeometry();
820
821
}

822
823
824
825
826
827
828
829
bool Toplevel::isLocalhost() const
{
    if (!m_clientMachine) {
        return true;
    }
    return m_clientMachine->isLocal();
}

830
831
832
833
834
QMargins Toplevel::bufferMargins() const
{
    return QMargins();
}

835
836
837
838
839
QMargins Toplevel::frameMargins() const
{
    return QMargins();
}

840
841
} // namespace