output.cpp 13.2 KB
Newer Older
1
2
/*************************************************************************************
 *  Copyright (C) 2012 by Alejandro Fiestas Olivares <afiestas@kde.org>              *
3
 *  Copyright (C) 2014 by Daniel Vrátil <dvratil@redhat.com>                         *
4
 *                                                                                   *
5
6
7
8
 *  This library is free software; you can redistribute it and/or                    *
 *  modify it under the terms of the GNU Lesser General Public                       *
 *  License as published by the Free Software Foundation; either                     *
 *  version 2.1 of the License, or (at your option) any later version.               *
9
 *                                                                                   *
10
 *  This library is distributed in the hope that it will be useful,                  *
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of                   *
12
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU                *
 *  Lesser General Public License for more details.                                  *
14
 *                                                                                   *
15
16
17
 *  You should have received a copy of the GNU Lesser General Public                 *
 *  License along with this library; if not, write to the Free Software              *
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA       *
18
19
20
21
 *************************************************************************************/

#include "output.h"
#include "mode.h"
Daniel Vrátil's avatar
Daniel Vrátil committed
22
#include "edid.h"
23
#include "abstractbackend.h"
24
25
#include "backendmanager_p.h"
#include "debug_p.h"
26

27
28
#include <QStringList>
#include <QPointer>
29
#include <QRect>
30

31
using namespace KScreen;
32

33
class Q_DECL_HIDDEN Output::Private
34
35
36
37
{
  public:
    Private():
        id(0),
Albert Astals Cid's avatar
Albert Astals Cid committed
38
        type(Unknown),
39
        rotation(None),
40
        scale(1.0),
41
42
43
        connected(false),
        enabled(false),
        primary(false),
Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
44
        edid(nullptr)
45
46
    {}

47
48
49
50
51
52
53
54
55
56
57
    Private(const Private &other):
        id(other.id),
        name(other.name),
        type(other.type),
        icon(other.icon),
        clones(other.clones),
        currentMode(other.currentMode),
        preferredMode(other.preferredMode),
        preferredModes(other.preferredModes),
        sizeMm(other.sizeMm),
        pos(other.pos),
58
        size(other.size),
59
        rotation(other.rotation),
60
        scale(other.scale),
61
62
63
64
        connected(other.connected),
        enabled(other.enabled),
        primary(other.primary)
    {
65
        Q_FOREACH (const ModePtr &otherMode, other.modeList) {
66
67
68
69
70
71
72
            modeList.insert(otherMode->id(), otherMode->clone());
        }
        if (other.edid) {
            edid = other.edid->clone();
        }
    }

73
    QString biggestMode(const ModeList& modes) const;
74
    bool compareModeList(const ModeList& before, const ModeList& after);
75

76
77
    int id;
    QString name;
78
    Type type;
79
80
81
    QString icon;
    ModeList modeList;
    QList<int> clones;
82
83
84
    QString currentMode;
    QString preferredMode;
    QStringList preferredModes;
85
    QSize sizeMm;
86
    QPoint pos;
87
    QSize size;
88
    Rotation rotation;
89
    qreal scale;
90
91
92
93
94
95
    bool connected;
    bool enabled;
    bool primary;

    mutable QPointer<Edid> edid;
};
96

97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
bool Output::Private::compareModeList(const ModeList& before, const ModeList &after)
{
    if (before.keys() != after.keys()) {
        return false;
    }
    for (const QString &key : before.keys()) {
        const auto mb = before.value(key);
        const auto ma = after.value(key);
        if (mb->id() != ma->id()) {
            return false;
        }
        if (mb->size() != ma->size()) {
            return false;
        }
        if (mb->refreshRate() != ma->refreshRate()) {
            return false;
        }
        if (mb->name() != ma->name()) {
            return false;
        }
    }
    // They're the same
    return true;
}


123
QString Output::Private::biggestMode(const ModeList& modes) const
124
125
{
    int area, total = 0;
126
127
    KScreen::ModePtr biggest;
    Q_FOREACH(const KScreen::ModePtr &mode, modes) {
128
129
130
131
        area = mode->size().width() * mode->size().height();
        if (area < total) {
            continue;
        }
132
133
134
        if (area == total && mode->refreshRate() < biggest->refreshRate()) {
            continue;
        }
135
136
137
138
139
140
141
142
143
144
        if (area == total && mode->refreshRate() > biggest->refreshRate()) {
            biggest = mode;
            continue;
        }

        total = area;
        biggest = mode;
    }

    if (!biggest) {
Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
145
        return nullptr;
146
147
148
149
150
    }

    return biggest->id();
}

151
Output::Output()
Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
152
 : QObject(nullptr)
153
 , d(new Private())
154
{
155

156
157
}

158
159
160
161
162
163
Output::Output(Output::Private *dd)
 : QObject()
 , d(dd)
{
}

164
Output::~Output()
165
{
166
    delete d;
167
168
}

169
OutputPtr Output::clone() const
170
{
171
    return OutputPtr(new Output(new Private(*d)));
172
173
}

174
175
int Output::id() const
{
176
    return d->id;
177
178
}

179
180
void Output::setId(int id)
{
181
    if (d->id == id) {
182
183
184
        return;
    }

185
    d->id = id;
186
187
188
189

    Q_EMIT outputChanged();
}

190
QString Output::name() const
191
{
192
    return d->name;
193
194
195
196
}

void Output::setName(const QString& name)
{
197
    if (d->name == name) {
198
199
200
        return;
    }

201
    d->name = name;
202
203

    Q_EMIT outputChanged();
204
205
}

206
Output::Type Output::type() const
207
{
208
    return d->type;
209
210
}

211
void Output::setType(Type type)
212
{
213
    if (d->type == type) {
214
215
216
        return;
    }

217
    d->type = type;
218
219

    Q_EMIT outputChanged();
220
221
222
223
}

QString Output::icon() const
{
224
    return d->icon;
225
226
227
228
}

void Output::setIcon(const QString& icon)
{
229
    if (d->icon == icon) {
230
231
232
        return;
    }

233
    d->icon = icon;
234
235

    Q_EMIT outputChanged();
236
237
}

238
ModePtr Output::mode(const QString& id) const
239
{
240
    if (!d->modeList.contains(id)) {
241
        return ModePtr();
242
243
    }

244
    return d->modeList[id];
245
246
}

247
ModeList Output::modes() const
248
{
249
    return d->modeList;
Àlex Fiestas's avatar
Àlex Fiestas committed
250
}
251

252
void Output::setModes(const ModeList &modes)
Àlex Fiestas's avatar
Àlex Fiestas committed
253
{
254
    bool changed = !d->compareModeList(d->modeList, modes);
255
    d->modeList = modes;
256
257
258
259
    if (changed) {
        emit modesChanged();
        emit outputChanged();
    }
260
261
}

262
QString Output::currentModeId() const
263
{
264
    return d->currentMode;
265
266
}

267
void Output::setCurrentModeId(const QString& mode)
Àlex Fiestas's avatar
Àlex Fiestas committed
268
{
269
    if (d->currentMode == mode) {
270
271
272
        return;
    }

273
    d->currentMode = mode;
274

275
276
277
    Q_EMIT currentModeIdChanged();
}

278
ModePtr Output::currentMode() const
279
280
{
    return d->modeList.value(d->currentMode);
Àlex Fiestas's avatar
Àlex Fiestas committed
281
282
}

283
void Output::setPreferredModes(const QStringList &modes)
Daniel Vrátil's avatar
Daniel Vrátil committed
284
{
285
    d->preferredMode = QString();
286
    d->preferredModes = modes;
Daniel Vrátil's avatar
Daniel Vrátil committed
287
288
}

289
QStringList Output::preferredModes() const
Daniel Vrátil's avatar
Daniel Vrátil committed
290
{
291
    return d->preferredModes;
Daniel Vrátil's avatar
Daniel Vrátil committed
292
293
}

294
QString Output::preferredModeId() const
Daniel Vrátil's avatar
Daniel Vrátil committed
295
{
296
    if (!d->preferredMode.isEmpty()) {
297
        return d->preferredMode;
298
    }
299
300
301
    if (d->preferredModes.isEmpty()) {
        return d->biggestMode(modes());
    }
302
303

    int area, total = 0;
304
305
    KScreen::ModePtr biggest;
    KScreen::ModePtr candidateMode;
306
    Q_FOREACH(const QString &modeId, d->preferredModes) {
307
308
        candidateMode = mode(modeId);
        area = candidateMode->size().width() * candidateMode->size().height();
309
310
311
        if (area < total) {
            continue;
        }
312
        if (area == total && biggest && candidateMode->refreshRate() < biggest->refreshRate()) {
313
314
            continue;
        }
315
316
        if (area == total && biggest && candidateMode->refreshRate() > biggest->refreshRate()) {
            biggest = candidateMode;
317
318
319
320
            continue;
        }

        total = area;
321
        biggest = candidateMode;
322
323
    }

324
    Q_ASSERT_X(biggest, "preferredModeId", "biggest mode must exist");
325

326
327
    d->preferredMode = biggest->id();
    return d->preferredMode;
Daniel Vrátil's avatar
Daniel Vrátil committed
328
329
}

330
ModePtr Output::preferredMode() const
331
332
333
334
{
    return d->modeList.value(preferredModeId());
}

335
336
QPoint Output::pos() const
{
337
    return d->pos;
338
339
340
341
}

void Output::setPos(const QPoint& pos)
{
342
    if (d->pos == pos) {
343
344
345
        return;
    }

346
    d->pos = pos;
347
348

    Q_EMIT posChanged();
349
350
}

351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
QSize Output::size() const
{
    return d->size;
}

void Output::setSize(const QSize& size)
{
    if (d->size == size) {
        return;
    }

    d->size = size;

    Q_EMIT sizeChanged();
}

367
368
Output::Rotation Output::rotation() const
{
369
    return d->rotation;
370
371
}

372
373
void Output::setRotation(Output::Rotation rotation)
{
374
    if (d->rotation == rotation) {
375
376
377
        return;
    }

378
    d->rotation = rotation;
379

380
    Q_EMIT rotationChanged();
381
382
}

383
384
385
386
387
388
389
390
391
392
393
394
395
396
qreal Output::scale() const
{
    return d->scale;
}

void Output::setScale(qreal factor)
{
    if (d->scale == factor) {
        return;
    }
    d->scale = factor;
    emit scaleChanged();
}

397
398
bool Output::isConnected() const
{
399
    return d->connected;
400
401
402
403
}

void Output::setConnected(bool connected)
{
404
    if (d->connected == connected) {
405
406
407
        return;
    }

408
    d->connected = connected;
409
410

    Q_EMIT isConnectedChanged();
411
412
413
414
}

bool Output::isEnabled() const
{
415
    return d->enabled;
416
417
418
419
}

void Output::setEnabled(bool enabled)
{
420
    if (d->enabled == enabled) {
421
422
423
        return;
    }

424
    d->enabled = enabled;
425
426

    Q_EMIT isEnabledChanged();
427
428
429
430
}

bool Output::isPrimary() const
{
431
    return d->primary;
432
433
434
435
}

void Output::setPrimary(bool primary)
{
436
    if (d->primary == primary) {
437
438
439
        return;
    }

440
    d->primary = primary;
441
442

    Q_EMIT isPrimaryChanged();
443
444
}

445
QList<int> Output::clones() const
446
{
447
    return d->clones;
Àlex Fiestas's avatar
Àlex Fiestas committed
448
449
}

450
void Output::setClones(QList<int> outputlist)
Àlex Fiestas's avatar
Àlex Fiestas committed
451
{
452
    if (d->clones == outputlist) {
453
454
455
        return;
    }

456
    d->clones = outputlist;
457
458

    Q_EMIT clonesChanged();
459
}
460

461
void Output::setEdid(const QByteArray& rawData)
Daniel Vrátil's avatar
Daniel Vrátil committed
462
{
463
    Q_ASSERT(d->edid.isNull());
464
465
466
467
468
    d->edid = new Edid(rawData);
}

Edid *Output::edid() const
{
469
    return d->edid;
Daniel Vrátil's avatar
Daniel Vrátil committed
470
471
}

472
473
474
475
476
477
478
479
480
481
QSize Output::sizeMm() const
{
    return d->sizeMm;
}

void Output::setSizeMm(const QSize &size)
{
    d->sizeMm = size;
}

482
483
QRect Output::geometry() const
{
484
485
486
487
488
489
490
    if (!currentMode()) {
        return QRect();
    }

    // We can't use QRect(d->pos, d->size), because d->size does not reflect the
    // actual rotation() set by caller, it's only updated when we get update from
    // KScreen, but not when user changes mode or rotation manually
491
492
493
494
495
496
497

    QSize size = currentMode()->size() / d->scale;
    if (!isHorizontal()) {
        size = size.transposed();
    }

    return QRect(d->pos, size);
498
499
}

500
501
void Output::apply(const OutputPtr& other)
{
502
503
504
505
    typedef void (KScreen::Output::*ChangeSignal)();
    QList<ChangeSignal> changes;

    // We block all signals, and emit them only after we have set up everything
Sebastian Kügler's avatar
Sebastian Kügler committed
506
    // This is necessary in order to prevent clients from accessing inconsistent
507
    // outputs from intermediate change signals
508
    const bool keepBlocked = signalsBlocked();
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
    blockSignals(true);
    if (d->name != other->d->name) {
        changes << &Output::outputChanged;
        setName(other->d->name);
    }
    if (d->type != other->d->type) {
        changes << &Output::outputChanged;
        setType(other->d->type);
    }
    if (d->icon != other->d->icon) {
        changes << &Output::outputChanged;
        setIcon(other->d->icon);
    }
    if (d->pos != other->d->pos) {
        changes << &Output::posChanged;
        setPos(other->pos());
    }
    if (d->rotation != other->d->rotation) {
        changes << &Output::rotationChanged;
        setRotation(other->d->rotation);
    }
530
531
532
533
    if (d->scale != other->d->scale) {
        changes << &Output::scaleChanged;
        setScale(other->d->scale);
    }
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
    if (d->currentMode != other->d->currentMode) {
        changes << &Output::currentModeIdChanged;
        setCurrentModeId(other->d->currentMode);
    }
    if (d->connected != other->d->connected) {
        changes << &Output::isConnectedChanged;
        setConnected(other->d->connected);
    }
    if (d->enabled != other->d->enabled) {
        changes << &Output::isEnabledChanged;
        setEnabled(other->d->enabled);
    }
    if (d->primary != other->d->primary) {
        changes << &Output::isPrimaryChanged;
        setPrimary(other->d->primary);
    }
    if (d->clones != other->d->clones) {
        changes << &Output::clonesChanged;
        setClones(other->d->clones);;
    }
554
555
556
    if (!d->compareModeList(d->modeList, other->d->modeList)) {
        changes << &Output::outputChanged;
    }
557
558
559
560
561
562
563
564

    setPreferredModes(other->d->preferredModes);
    ModeList modes;
    Q_FOREACH (const ModePtr &otherMode, other->modes()) {
        modes.insert(otherMode->id(), otherMode->clone());
    }
    setModes(modes);

565
    // Non-notifyable changes
566
    if (other->d->edid) {
567
568
569
570
        delete d->edid;
        d->edid = other->d->edid->clone();
    }

571
    blockSignals(keepBlocked);
572
573

    while (!changes.isEmpty()) {
574
575
        const ChangeSignal &sig = changes.first();
        Q_EMIT (this->*sig)();
576
577
        changes.removeAll(sig);
    }
578
579
}

580
QDebug operator<<(QDebug dbg, const KScreen::OutputPtr &output)
581
{
582
    if(output) {
583
584
585
586
        dbg << "KScreen::Output(" << output->id() << " "
                                  << output->name()
                                  << (output->isConnected() ? "connected" : "disconnected")
                                  << (output->isEnabled() ? "enabled" : "disabled")
587
                                  << (output->isPrimary() ? "primary" : "")
588
589
590
                                  << "pos:" << output->pos() << "res:" << output->size()
                                  << "modeId:" << output->currentModeId()
                                  << "scale:" << output->scale()
591
                                  << ")";
592
593
594
    } else {
        dbg << "KScreen::Output(NULL)";
    }
595
596
    return dbg;
}