qmlscreen.cpp 9.53 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
 * Copyright (C) 2013  Daniel Vrátil <dvratil@redhat.com>
 *
 * 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, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */

#include "qmlscreen.h"
#include "qmloutputcomponent.h"
#include "qmloutput.h"

#include <kscreen/output.h>
#include <kscreen/config.h>
26
27

#include <QTimer>
28
#include <sys/socket.h>
29

Frederik Gladhorn's avatar
Frederik Gladhorn committed
30
31
QMLScreen::QMLScreen(QQuickItem *parent)
    : QQuickItem(parent)
32
{
33
34
    connect(this, &QMLScreen::widthChanged, this, &QMLScreen::viewSizeChanged);
    connect(this, &QMLScreen::heightChanged, this, &QMLScreen::viewSizeChanged);
35
36
}

37
38
39
40
41
42
43
44
45
KScreen::ConfigPtr QMLScreen::config() const
{
    return m_config;
}

void QMLScreen::setConfig(const KScreen::ConfigPtr &config)
{
    qDeleteAll(m_outputMap);
    m_outputMap.clear();
Laurent Montel's avatar
Laurent Montel committed
46
    m_bottommost = m_leftmost = m_rightmost = m_topmost = nullptr;
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
    m_connectedOutputsCount = 0;
    m_enabledOutputsCount = 0;

    if (m_config) {
        m_config->disconnect(this);
    }

    m_config = config;
    connect(m_config.data(), &KScreen::Config::outputAdded,
            this, [this](const KScreen::OutputPtr &output) {
                addOutput(output);
                updateOutputsPlacement();
            });
    connect(m_config.data(), &KScreen::Config::outputRemoved,
            this, &QMLScreen::removeOutput);

    for (const KScreen::OutputPtr &output : m_config->outputs()) {
        addOutput(output);
    }

    updateOutputsPlacement();

    for (QMLOutput *qmlOutput : m_outputMap) {
        if (qmlOutput->output()->isConnected() && qmlOutput->output()->isEnabled()) {
            qmlOutput->dockToNeighbours();
        }
    }
}


77
void QMLScreen::addOutput(const KScreen::OutputPtr &output)
78
{
79
    //QQuickItem *container = findChild<QQuickItem*>(QLatin1String("outputContainer"));
80

81
    QMLOutputComponent comp(m_engine, this);
82
83
    QMLOutput *qmloutput = comp.createForOutput(output);
    if (!qmloutput) {
84
        qWarning() << "Failed to create QMLOutput";
85
86
87
88
89
90
        return;
    }

    m_outputMap.insert(output, qmloutput);

    qmloutput->setParentItem(this);
91
    qmloutput->setZ(m_outputMap.count());
92

93
94
95
96
97
98
99
100
101
102
103
104
105
106
    connect(output.data(), &KScreen::Output::isConnectedChanged,
            this, &QMLScreen::outputConnectedChanged);
    connect(output.data(), &KScreen::Output::isEnabledChanged,
            this, &QMLScreen::outputEnabledChanged);
    connect(output.data(), &KScreen::Output::posChanged,
            this, &QMLScreen::outputPositionChanged);
    connect(qmloutput, &QMLOutput::yChanged,
            [this, qmloutput]() {
                qmlOutputMoved(qmloutput);
            });
    connect(qmloutput, &QMLOutput::xChanged,
            [this, qmloutput]() {
                qmlOutputMoved(qmloutput);
            });
107
    connect(qmloutput, SIGNAL(clicked()),
108
            this, SLOT(setActiveOutput()));
109

110
    qmloutput->updateRootProperties();
111
112
}

113
114
115
116
117
void QMLScreen::removeOutput(int outputId)
{
    for (const KScreen::OutputPtr &output : m_outputMap.keys()) {
        if (output->id() == outputId) {
            QMLOutput *qmlOutput = m_outputMap.take(output);
118
119
            qmlOutput->setParentItem(nullptr);
            qmlOutput->setParent(nullptr);
120
121
122
123
124
125
126
            qmlOutput->deleteLater();
            return;
        }
    }
}


127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
int QMLScreen::connectedOutputsCount() const
{
    return m_connectedOutputsCount;
}

int QMLScreen::enabledOutputsCount() const
{
    return m_enabledOutputsCount;
}

QMLOutput *QMLScreen::primaryOutput() const
{
    Q_FOREACH (QMLOutput *qmlOutput, m_outputMap) {
        if (qmlOutput->output()->isPrimary()) {
            return qmlOutput;
        }
    }

145
    return nullptr;
146
147
}

Daniel Vrátil's avatar
Daniel Vrátil committed
148
149
150
151
152
153
QList<QMLOutput*> QMLScreen::outputs() const
{
    return m_outputMap.values();
}


154
void QMLScreen::setActiveOutput(QMLOutput *output)
155
156
{
    Q_FOREACH (QMLOutput *qmlOutput, m_outputMap) {
157
        if (qmlOutput->z() > output->z()) {
158
            qmlOutput->setZ(qmlOutput->z() - 1);
159
160
161
        }
    }

162
163
164
    output->setZ(m_outputMap.count());
    output->setFocus(true);
    Q_EMIT focusedOutputChanged(output);
165
166
}

167
168
QSize QMLScreen::maxScreenSize() const
{
169
170
171
172
173
    return m_config->screen()->maxSize();
}

float QMLScreen::outputScale() const
{
174
    return 1.0 / 8.0;
175
176
177
178
179
180
}

void QMLScreen::outputConnectedChanged()
{
    int connectedCount = 0;

181
    Q_FOREACH (const KScreen::OutputPtr &output, m_outputMap.keys()) {
182
183
184
185
186
187
188
189
        if (output->isConnected()) {
            ++connectedCount;
        }
    }

    if (connectedCount != m_connectedOutputsCount) {
        m_connectedOutputsCount = connectedCount;
        Q_EMIT connectedOutputsCountChanged();
190
        updateOutputsPlacement();
191
192
193
194
195
    }
}

void QMLScreen::outputEnabledChanged()
{
196
    const KScreen::OutputPtr output(qobject_cast<KScreen::Output*>(sender()), [](void *){});
197
198
199
    if (output->isEnabled()) {
        updateOutputsPlacement();
    }
200
201
    int enabledCount = 0;

202
    Q_FOREACH (const KScreen::OutputPtr &output, m_outputMap.keys()) {
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
        if (output->isEnabled()) {
            ++enabledCount;
        }
    }

    if (enabledCount == m_enabledOutputsCount) {
        m_enabledOutputsCount = enabledCount;
        Q_EMIT enabledOutputsCountChanged();
    }
}

void QMLScreen::outputPositionChanged()
{
    /* TODO: Reposition the QMLOutputs */
}

void QMLScreen::qmlOutputMoved(QMLOutput *qmlOutput)
{
221
222
223
224
    if (qmlOutput->isCloneMode()) {
        return;
    }

225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
    updateCornerOutputs();

    if (m_leftmost) {
        m_leftmost->setOutputX(0);
    }
    if (m_topmost) {
        m_topmost->setOutputY(0);
    }

    if (qmlOutput == m_leftmost) {
        Q_FOREACH (QMLOutput *other, m_outputMap) {
            if (other == m_leftmost) {
                continue;
            }

            if (!other->output()->isConnected() || !other->output()->isEnabled()) {
                continue;
            }

244
            other->setOutputX(float(other->x() - m_leftmost->x()) / outputScale());
245
246
        }
    } else if (m_leftmost) {
247
        qmlOutput->setOutputX(float(qmlOutput->x() - m_leftmost->x()) / outputScale());
248
249
250
251
252
253
254
255
256
257
258
259
    }

    if (qmlOutput == m_topmost) {
        Q_FOREACH (QMLOutput *other, m_outputMap) {
            if (other == m_topmost) {
                continue;
            }

            if (!other->output()->isConnected() || !other->output()->isEnabled()) {
                continue;
            }

260
            other->setOutputY(float(other->y() - m_topmost->y()) / outputScale());
261
262
        }
    } else if (m_topmost) {
263
        qmlOutput->setOutputY(float(qmlOutput->y() - m_topmost->y()) / outputScale());
264
265
266
    }
}

267
268
269
270
void QMLScreen::viewSizeChanged()
{
    updateOutputsPlacement();
}
271
272
273

void QMLScreen::updateCornerOutputs()
{
Laurent Montel's avatar
Laurent Montel committed
274
275
276
277
    m_leftmost = nullptr;
    m_topmost = nullptr;
    m_rightmost = nullptr;
    m_bottommost = nullptr;
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302

    Q_FOREACH (QMLOutput *output, m_outputMap) {
        if (!output->output()->isConnected() || !output->output()->isEnabled()) {
            continue;
        }

        QMLOutput *other = m_leftmost;
        if (!other || output->x() < other->x()) {
            m_leftmost = output;
        }

        if (!other || output->y() < other->y()) {
            m_topmost = output;
        }

        if (!other || output->x() + output->width() > other->x() + other->width()) {
            m_rightmost = output;
        }

        if (!other || output->y() + output->height() > other->y() + other->height()) {
            m_bottommost = output;
        }
    }
}

303
void QMLScreen::updateOutputsPlacement()
304
305
306
307
{
    int disabledOffsetX = width();
    QSizeF activeScreenSize;

308
    Q_FOREACH (QQuickItem *item, childItems()) {
309
        QMLOutput *qmlOutput = qobject_cast<QMLOutput*>(item);
310
311
312
313
314
        if (!qmlOutput->output()->isConnected()) {
            continue;
        }

        if (!qmlOutput->output()->isEnabled()) {
315
            qmlOutput->blockSignals(true);
316
            disabledOffsetX -= qmlOutput->width();
317
            qmlOutput->setPosition(QPoint(disabledOffsetX, 0));
318
            qmlOutput->blockSignals(false);
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
            continue;
        }

        if (qmlOutput->outputX() + qmlOutput->currentOutputWidth() > activeScreenSize.width()) {
            activeScreenSize.setWidth(qmlOutput->outputX() + qmlOutput->currentOutputWidth());
        }
        if (qmlOutput->outputY() + qmlOutput->currentOutputHeight() > activeScreenSize.height()) {
            activeScreenSize.setHeight(qmlOutput->outputY() + qmlOutput->currentOutputHeight());
        }
    }

    activeScreenSize *= outputScale();

    const QPointF offset((width() - activeScreenSize.width()) / 2.0,
                         (height() - activeScreenSize.height()) / 2.0);

335
    Q_FOREACH (QQuickItem *item, childItems()) {
336
        QMLOutput *qmlOutput = qobject_cast<QMLOutput*>(item);
337
338
339
340
341
        if (!qmlOutput->output()->isConnected() || !qmlOutput->output()->isEnabled()) {
            continue;
        }

        qmlOutput->blockSignals(true);
342
343
        qmlOutput->setPosition(QPointF(offset.x() + (qmlOutput->outputX() * outputScale()),
                          offset.y() + (qmlOutput->outputY() * outputScale())));
344
345
346
347
        qmlOutput->blockSignals(false);
    }
}

348
349
350
351
void QMLScreen::setEngine(QQmlEngine* engine)
{
    m_engine = engine;
}