toplevel.cpp 19 KB
Newer Older
1
/********************************************************************
2
3
4
5
6
 KWin - the KDE window manager
 This file is part of the KDE project.

Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org>

7
8
9
10
11
12
13
14
15
16
17
18
19
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/>.
*********************************************************************/
20
21
#include "toplevel.h"

22
23
24
#ifdef KWIN_BUILD_ACTIVITIES
#include "activities.h"
#endif
25
26
#include "atoms.h"
#include "client.h"
27
#include "client_machine.h"
28
#include "composite.h"
29
#include "effects.h"
30
#include "screens.h"
31
#include "shadow.h"
32
#include "workspace.h"
33
#include "xcbutils.h"
34

35
36
#include <KWayland/Server/surface_interface.h>

Martin Flöser's avatar
Martin Flöser committed
37
38
#include <QDebug>

39
40
41
namespace KWin
{

42
Toplevel::Toplevel()
43
    : m_visual(XCB_NONE)
44
    , bit_depth(24)
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
45
    , info(nullptr)
46
    , ready_for_painting(false)
Laurent Montel's avatar
Laurent Montel committed
47
    , m_isDamaged(false)
48
    , m_internalId(QUuid::createUuid())
49
    , m_client()
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
50
    , damage_handle(XCB_NONE)
51
    , is_shape(false)
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
52
    , effect_window(nullptr)
53
    , m_clientMachine(new ClientMachine(this))
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
54
    , m_wmClientLeader(XCB_WINDOW_NONE)
55
    , m_damageReplyPending(false)
56
    , m_screen(0)
57
    , m_skipCloseAnimation(false)
58
{
59
    connect(this, SIGNAL(damaged(KWin::Toplevel*,QRect)), SIGNAL(needsRepaint()));
60
    connect(screens(), SIGNAL(changed()), SLOT(checkScreen()));
61
    connect(screens(), SIGNAL(countChanged(int,int)), SLOT(checkScreen()));
62
    setupCheckScreenConnection();
63
}
64
65

Toplevel::~Toplevel()
66
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
67
    Q_ASSERT(damage_handle == XCB_NONE);
68
    delete info;
69
}
70

71
72
QDebug& operator<<(QDebug& stream, const Toplevel* cl)
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
73
    if (cl == nullptr)
74
        return stream << "\'NULL\'";
75
    cl->debug(stream);
76
    return stream;
77
}
78

79
80
QDebug& operator<<(QDebug& stream, const ToplevelList& list)
{
81
82
    stream << "LIST:(";
    bool first = true;
83
84
85
86
    for (ToplevelList::ConstIterator it = list.begin();
            it != list.end();
            ++it) {
        if (!first)
87
88
89
            stream << ":";
        first = false;
        stream << *it;
90
    }
91
92
    stream << ")";
    return stream;
93
}
94

Thomas Lübking's avatar
Thomas Lübking committed
95
96
QRect Toplevel::decorationRect() const
{
97
    return rect();
Thomas Lübking's avatar
Thomas Lübking committed
98
99
}

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
100
void Toplevel::detectShape(xcb_window_t id)
101
{
102
    const bool wasShape = is_shape;
103
    is_shape = Xcb::Extensions::self()->hasShape(id);
104
105
106
    if (wasShape != is_shape) {
        emit shapedChanged();
    }
107
}
108
109

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

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

145
QRect Toplevel::visibleRect() const
146
{
147
    QRect r = decorationRect();
148
    if (hasShadow() && !shadow()->shadowRegion().isEmpty()) {
149
        r |= shadow()->shadowRegion().boundingRect();
150
    }
151
    return r.translated(geometry().topLeft());
152
}
153

154
155
156
157
158
159
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)
160
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
161
    m_wmClientLeader = prop.value<xcb_window_t>(window());
162
}
163

164
165
166
167
168
169
void Toplevel::getWmClientLeader()
{
    auto prop = fetchWmClientLeader();
    readWmClientLeader(prop);
}

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

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

197
void Toplevel::getWmClientMachine()
198
{
199
    m_clientMachine->resolve(window(), wmClientLeader());
200
}
201

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

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

void Toplevel::getResourceClass()
232
{
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

243
double Toplevel::opacity() const
244
245
{
    if (info->opacity() == 0xffffffff)
246
247
        return 1.0;
    return info->opacity() * 1.0 / 0xffffffff;
248
}
249

250
251
void Toplevel::setOpacity(double new_opacity)
{
252
    double old_opacity = opacity();
253
254
    new_opacity = qBound(0.0, new_opacity, 1.0);
    if (old_opacity == new_opacity)
255
        return;
256
257
    info->setOpacity(static_cast< unsigned long >(new_opacity * 0xffffffff));
    if (compositing()) {
258
        addRepaintFull();
259
        emit opacityChanged(this, old_opacity);
260
    }
261
}
262

263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
bool Toplevel::setupCompositing()
{
    if (!compositing())
        return false;

    if (damage_handle != XCB_NONE)
        return false;

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

    damage_region = QRegion(0, 0, width(), height());
    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();
    repaints_region = QRegion();
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
301
    effect_window = nullptr;
302
303
304
305
306
}

void Toplevel::discardWindowPixmap()
{
    addDamageFull();
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
307
    if (effectWindow() != nullptr && effectWindow()->sceneWindow() != nullptr)
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
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
355
356
357
358
359
360
361
        effectWindow()->sceneWindow()->pixmapDiscarded();
}

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

    // Note: The rect is supposed to specify the damage extents,
    //       but we don't know it at this point. No one who connects
    //       to this signal uses the rect however.
    emit damaged(this, QRect());
}

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

void Client::damageNotifyEvent()
{
    if (syncRequest.isPending && isResize()) {
        emit damaged(this, QRect());
        m_isDamaged = true;
        return;
    }

    if (!ready_for_painting) { // avoid "setReadyForPainting()" function calling overhead
        if (syncRequest.counter == XCB_NONE) {  // cannot detect complete redraw, consider done now
            setReadyForPainting();
            setupWindowManagementInterface();
        }
    }

    Toplevel::damageNotifyEvent();
}

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
362
    xcb_xfixes_create_region(conn, region, 0, nullptr);
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
    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
384
            xcb_xfixes_fetch_region_reply(connection(), m_regionCookie, nullptr);
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
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

    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);

    damage_region += region;
    repaints_region += region;

    free(reply);
}

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

    damage_region = rect();
    repaints_region |= rect();

    emit damaged(this, rect());
}

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

void Toplevel::addRepaint(const QRect& r)
{
    if (!compositing()) {
        return;
    }
    repaints_region += r;
    emit needsRepaint();
}

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

void Toplevel::addRepaint(const QRegion& r)
{
    if (!compositing()) {
        return;
    }
    repaints_region += r;
    emit needsRepaint();
}

void Toplevel::addLayerRepaint(const QRect& r)
{
    if (!compositing()) {
        return;
    }
    layer_repaints_region += r;
    emit needsRepaint();
}

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

void Toplevel::addLayerRepaint(const QRegion& r)
{
    if (!compositing())
        return;
    layer_repaints_region += r;
    emit needsRepaint();
}

void Toplevel::addRepaintFull()
{
    repaints_region = visibleRect().translated(-pos());
    emit needsRepaint();
}

void Toplevel::resetRepaints()
{
    repaints_region = QRegion();
    layer_repaints_region = QRegion();
}

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);
}

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

511
void Toplevel::deleteEffectWindow()
512
{
513
    delete effect_window;
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
514
    effect_window = nullptr;
515
}
516

517
void Toplevel::checkScreen()
518
{
519
    if (screens()->count() == 1) {
520
521
522
523
        if (m_screen != 0) {
            m_screen = 0;
            emit screenChanged();
        }
524
525
526
527
528
529
    } else {
        const int s = screens()->number(geometry().center());
        if (s != m_screen) {
            m_screen = s;
            emit screenChanged();
        }
530
    }
531
532
533
534
    qreal newScale = screens()->scale(m_screen);
    if (newScale != m_screenScale) {
        m_screenScale = newScale;
        emit screenScaleChanged();
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
    }
}

void Toplevel::setupCheckScreenConnection()
{
    connect(this, SIGNAL(geometryShapeChanged(KWin::Toplevel*,QRect)), SLOT(checkScreen()));
    connect(this, SIGNAL(geometryChanged()), SLOT(checkScreen()));
    checkScreen();
}

void Toplevel::removeCheckScreenConnection()
{
    disconnect(this, SIGNAL(geometryShapeChanged(KWin::Toplevel*,QRect)), this, SLOT(checkScreen()));
    disconnect(this, SIGNAL(geometryChanged()), this, SLOT(checkScreen()));
}

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

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

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

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

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

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

bool Toplevel::hasShadow() const
{
601
    if (effectWindow() && effectWindow()->sceneWindow()) {
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
602
        return effectWindow()->sceneWindow()->shadow() != nullptr;
603
604
    }
    return false;
605
606
}

607
Shadow *Toplevel::shadow()
608
{
609
610
611
    if (effectWindow() && effectWindow()->sceneWindow()) {
        return effectWindow()->sceneWindow()->shadow();
    } else {
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
612
        return nullptr;
613
614
615
616
617
618
619
620
    }
}

const Shadow *Toplevel::shadow() const
{
    if (effectWindow() && effectWindow()->sceneWindow()) {
        return effectWindow()->sceneWindow()->shadow();
    } else {
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
621
        return nullptr;
622
    }
623
}
624

625
626
627
628
629
bool Toplevel::wantsShadowToBeRendered() const
{
    return true;
}

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

    opaque_region = new_opaque_region;
}

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

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

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

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

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

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

682
683
684
685
686
687
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)
688
{
689
    setSkipCloseAnimation(property.toBool());
690
691
}

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

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

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

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

void Toplevel::addDamage(const QRegion &damage)
{
    m_isDamaged = true;
    damage_region += damage;
746
    for (const QRect &r : damage) {
747
748
749
750
        emit damaged(this, r);
    }
}

751
752
753
754
755
QByteArray Toplevel::windowRole() const
{
    return QByteArray(info->windowRole());
}

756
757
758
759
760
761
762
763
764
765
766
767
void Toplevel::setDepth(int depth)
{
    if (bit_depth == depth) {
        return;
    }
    const bool oldAlpha = hasAlpha();
    bit_depth = depth;
    if (oldAlpha != hasAlpha()) {
        emit hasAlphaChanged();
    }
}

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

778
779
780
781
782
783
784
QMatrix4x4 Toplevel::inputTransformation() const
{
    QMatrix4x4 m;
    m.translate(-x(), -y());
    return m;
}

785
786
787
788
789
quint32 Toplevel::windowId() const
{
    return window();
}

790
791
792
793
794
QRect Toplevel::inputGeometry() const
{
    return geometry();
}

795
796
797
798
799
800
801
802
bool Toplevel::isLocalhost() const
{
    if (!m_clientMachine) {
        return true;
    }
    return m_clientMachine->isLocal();
}

803
804
} // namespace