output_interface.cpp 15.3 KB
Newer Older
1
/********************************************************************
Martin Flöser's avatar
Martin Flöser committed
2
Copyright 2014  Martin Gräßlin <mgraesslin@kde.org>
3

Martin Flöser's avatar
Martin Flöser committed
4 5 6 7 8 9 10
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) version 3, or any
later version accepted by the membership of KDE e.V. (or its
successor approved by the membership of KDE e.V.), which shall
act as a proxy defined in Section 6 of version 3 of the license.
11

Martin Flöser's avatar
Martin Flöser committed
12
This library is distributed in the hope that it will be useful,
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
Martin Flöser's avatar
Martin Flöser committed
14 15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.
16

Martin Flöser's avatar
Martin Flöser committed
17 18
You should have received a copy of the GNU Lesser General Public
License along with this library.  If not, see <http://www.gnu.org/licenses/>.
19 20
*********************************************************************/
#include "output_interface.h"
21
#include "global_p.h"
22 23
#include "display.h"

24 25
#include <QVector>

26 27
#include <wayland-server.h>

28
namespace KWayland
29
{
30
namespace Server
31 32
{

33
class OutputInterface::Private : public Global::Private
34 35 36 37 38 39 40
{
public:
    struct ResourceData {
        wl_resource *resource;
        uint32_t version;
    };
    Private(OutputInterface *q, Display *d);
41
    ~Private();
42 43 44 45 46 47 48 49 50 51 52 53 54 55
    void sendMode(wl_resource *resource, const Mode &mode);
    void sendDone(const ResourceData &data);
    void updateGeometry();
    void updateScale();

    QSize physicalSize;
    QPoint globalPosition;
    QString manufacturer = QStringLiteral("org.kde.kwin");
    QString model = QStringLiteral("none");
    int scale = 1;
    SubPixel subPixel = SubPixel::Unknown;
    Transform transform = Transform::Normal;
    QList<Mode> modes;
    QList<ResourceData> resources;
56 57 58 59
    struct {
        DpmsMode mode = DpmsMode::On;
        bool supported = false;
    } dpms;
60

61 62
    static OutputInterface *get(wl_resource *native);

63
private:
64
    static Private *cast(wl_resource *native);
65
    static void releaseCallback(wl_client *client, wl_resource *resource);
66
    static void unbind(wl_resource *resource);
67
    void bind(wl_client *client, uint32_t version, uint32_t id) override;
68 69 70 71 72 73
    int32_t toTransform() const;
    int32_t toSubPixel() const;
    void sendGeometry(wl_resource *resource);
    void sendScale(const ResourceData &data);

    OutputInterface *q;
74
    static QVector<Private*> s_privates;
75
    static const struct wl_output_interface s_interface;
76
    static const quint32 s_version;
77 78
};

79
QVector<OutputInterface::Private*> OutputInterface::Private::s_privates;
80
const quint32 OutputInterface::Private::s_version = 3;
81

82
OutputInterface::Private::Private(OutputInterface *q, Display *d)
83
    : Global::Private(d, &wl_output_interface, s_version)
84 85
    , q(q)
{
86 87 88 89 90 91 92 93
    s_privates << this;
}

OutputInterface::Private::~Private()
{
    s_privates.removeAll(this);
}

94 95 96 97 98 99 100 101 102 103 104 105
#ifndef DOXYGEN_SHOULD_SKIP_THIS
const struct wl_output_interface OutputInterface::Private::s_interface = {
    releaseCallback
};
#endif

void OutputInterface::Private::releaseCallback(wl_client *client, wl_resource *resource)
{
    Q_UNUSED(client);
    unbind(resource);
}

106
OutputInterface *OutputInterface::Private::get(wl_resource *native)
107 108 109 110 111 112 113 114
{
    if (Private *p = cast(native)) {
        return p->q;
    }
    return nullptr;
}

OutputInterface::Private *OutputInterface::Private::cast(wl_resource *native)
115 116 117
{
    for (auto it = s_privates.constBegin(); it != s_privates.constEnd(); ++it) {
        const auto &resources = (*it)->resources;
Frederik Gladhorn's avatar
Frederik Gladhorn committed
118 119
        auto rit = std::find_if(resources.constBegin(), resources.constEnd(), [native] (const ResourceData &data) { return data.resource == native; });
        if (rit != resources.constEnd()) {
120
            return (*it);
121 122 123
        }
    }
    return nullptr;
124 125
}

126
OutputInterface::OutputInterface(Display *display, QObject *parent)
127
    : Global(new Private(this, display), parent)
128
{
129
    Q_D();
130
    connect(this, &OutputInterface::currentModeChanged, this,
131
        [this, d] {
132 133
            auto currentModeIt = std::find_if(d->modes.constBegin(), d->modes.constEnd(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Current); });
            if (currentModeIt == d->modes.constEnd()) {
134 135
                return;
            }
136 137 138
            for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) {
                d->sendMode((*it).resource, *currentModeIt);
                d->sendDone(*it);
139
            }
140
            wl_display_flush_clients(*(d->display));
141 142
        }
    );
143 144 145 146 147 148
    connect(this, &OutputInterface::subPixelChanged,       this, [this, d] { d->updateGeometry(); });
    connect(this, &OutputInterface::transformChanged,      this, [this, d] { d->updateGeometry(); });
    connect(this, &OutputInterface::globalPositionChanged, this, [this, d] { d->updateGeometry(); });
    connect(this, &OutputInterface::modelChanged,          this, [this, d] { d->updateGeometry(); });
    connect(this, &OutputInterface::manufacturerChanged,   this, [this, d] { d->updateGeometry(); });
    connect(this, &OutputInterface::scaleChanged,          this, [this, d] { d->updateScale(); });
149 150
}

151
OutputInterface::~OutputInterface() = default;
152 153 154

QSize OutputInterface::pixelSize() const
{
155
    Q_D();
Frederik Gladhorn's avatar
Frederik Gladhorn committed
156
    auto it = std::find_if(d->modes.constBegin(), d->modes.constEnd(),
157 158 159 160
        [](const Mode &mode) {
            return mode.flags.testFlag(ModeFlag::Current);
        }
    );
Frederik Gladhorn's avatar
Frederik Gladhorn committed
161
    if (it == d->modes.constEnd()) {
162 163 164 165 166 167 168
        return QSize();
    }
    return (*it).size;
}

int OutputInterface::refreshRate() const
{
169
    Q_D();
Frederik Gladhorn's avatar
Frederik Gladhorn committed
170
    auto it = std::find_if(d->modes.constBegin(), d->modes.constEnd(),
171 172 173 174
        [](const Mode &mode) {
            return mode.flags.testFlag(ModeFlag::Current);
        }
    );
Frederik Gladhorn's avatar
Frederik Gladhorn committed
175
    if (it == d->modes.constEnd()) {
176 177 178 179 180 181 182 183
        return 60000;
    }
    return (*it).refreshRate;
}

void OutputInterface::addMode(const QSize &size, OutputInterface::ModeFlags flags, int refreshRate)
{
    Q_ASSERT(!isValid());
184
    Q_D();
185

186
    auto currentModeIt = std::find_if(d->modes.begin(), d->modes.end(),
187 188 189 190
        [](const Mode &mode) {
            return mode.flags.testFlag(ModeFlag::Current);
        }
    );
191
    if (currentModeIt == d->modes.end() && !flags.testFlag(ModeFlag::Current)) {
192 193 194
        // no mode with current flag - enforce
        flags |= ModeFlag::Current;
    }
195
    if (currentModeIt != d->modes.end() && flags.testFlag(ModeFlag::Current)) {
196 197 198 199 200 201
        // another mode has the current flag - remove
        (*currentModeIt).flags &= ~uint(ModeFlag::Current);
    }

    if (flags.testFlag(ModeFlag::Preferred)) {
        // remove from existing Preferred mode
202
        auto preferredIt = std::find_if(d->modes.begin(), d->modes.end(),
203 204 205 206
            [](const Mode &mode) {
                return mode.flags.testFlag(ModeFlag::Preferred);
            }
        );
207
        if (preferredIt != d->modes.end()) {
208 209 210 211
            (*preferredIt).flags &= ~uint(ModeFlag::Preferred);
        }
    }

212
    auto existingModeIt = std::find_if(d->modes.begin(), d->modes.end(),
213 214 215 216 217 218 219 220 221 222 223 224
        [size,refreshRate](const Mode &mode) {
            return mode.size == size && mode.refreshRate == refreshRate;
        }
    );
    auto emitChanges = [this,flags,size,refreshRate] {
        emit modesChanged();
        if (flags.testFlag(ModeFlag::Current)) {
            emit refreshRateChanged(refreshRate);
            emit pixelSizeChanged(size);
            emit currentModeChanged();
        }
    };
225
    if (existingModeIt != d->modes.end()) {
226 227 228 229 230 231 232 233 234 235 236 237
        if ((*existingModeIt).flags == flags) {
            // nothing to do
            return;
        }
        (*existingModeIt).flags = flags;
        emitChanges();
        return;
    }
    Mode mode;
    mode.size = size;
    mode.refreshRate = refreshRate;
    mode.flags = flags;
238
    d->modes << mode;
239 240 241 242 243
    emitChanges();
}

void OutputInterface::setCurrentMode(const QSize &size, int refreshRate)
{
244
    Q_D();
245
    auto currentModeIt = std::find_if(d->modes.begin(), d->modes.end(),
246 247 248 249
        [](const Mode &mode) {
            return mode.flags.testFlag(ModeFlag::Current);
        }
    );
250
    if (currentModeIt != d->modes.end()) {
251 252 253 254
        // another mode has the current flag - remove
        (*currentModeIt).flags &= ~uint(ModeFlag::Current);
    }

255
    auto existingModeIt = std::find_if(d->modes.begin(), d->modes.end(),
256 257 258 259 260
        [size,refreshRate](const Mode &mode) {
            return mode.size == size && mode.refreshRate == refreshRate;
        }
    );

261
    Q_ASSERT(existingModeIt != d->modes.end());
262 263 264 265 266 267 268
    (*existingModeIt).flags |= ModeFlag::Current;
    emit modesChanged();
    emit refreshRateChanged((*existingModeIt).refreshRate);
    emit pixelSizeChanged((*existingModeIt).size);
    emit currentModeChanged();
}

269
int32_t OutputInterface::Private::toTransform() const
270
{
271
    switch (transform) {
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
    case Transform::Normal:
        return WL_OUTPUT_TRANSFORM_NORMAL;
    case Transform::Rotated90:
        return WL_OUTPUT_TRANSFORM_90;
    case Transform::Rotated180:
        return WL_OUTPUT_TRANSFORM_180;
    case Transform::Rotated270:
        return WL_OUTPUT_TRANSFORM_270;
    case Transform::Flipped:
        return WL_OUTPUT_TRANSFORM_FLIPPED;
    case Transform::Flipped90:
        return WL_OUTPUT_TRANSFORM_FLIPPED_90;
    case Transform::Flipped180:
        return WL_OUTPUT_TRANSFORM_FLIPPED_180;
    case Transform::Flipped270:
        return WL_OUTPUT_TRANSFORM_FLIPPED_270;
    }
    abort();
}

292
int32_t OutputInterface::Private::toSubPixel() const
293
{
294
    switch (subPixel) {
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
    case SubPixel::Unknown:
        return WL_OUTPUT_SUBPIXEL_UNKNOWN;
    case SubPixel::None:
        return WL_OUTPUT_SUBPIXEL_NONE;
    case SubPixel::HorizontalRGB:
        return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB;
    case SubPixel::HorizontalBGR:
        return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR;
    case SubPixel::VerticalRGB:
        return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB;
    case SubPixel::VerticalBGR:
        return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR;
    }
    abort();
}

311
void OutputInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id)
312
{
313 314
    auto c = display->getConnection(client);
    wl_resource *resource = c->createResource(&wl_output_interface, qMin(version, s_version), id);
315 316 317 318 319
    if (!resource) {
        wl_client_post_no_memory(client);
        return;
    }
    wl_resource_set_user_data(resource, this);
320
    wl_resource_set_implementation(resource, &s_interface, this, unbind);
321 322 323
    ResourceData r;
    r.resource = resource;
    r.version = version;
324
    resources << r;
325 326 327 328

    sendGeometry(resource);
    sendScale(r);

329 330
    auto currentModeIt = modes.constEnd();
    for (auto it = modes.constBegin(); it != modes.constEnd(); ++it) {
331 332 333 334 335 336 337 338 339
        const Mode &mode = *it;
        if (mode.flags.testFlag(ModeFlag::Current)) {
            // needs to be sent as last mode
            currentModeIt = it;
            continue;
        }
        sendMode(resource, mode);
    }

340
    if (currentModeIt != modes.constEnd()) {
341 342 343 344
        sendMode(resource, *currentModeIt);
    }

    sendDone(r);
345
    c->flush();
346 347
}

348
void OutputInterface::Private::unbind(wl_resource *resource)
349
{
350 351 352 353
    Private *o = cast(resource);
    if (!o) {
        return;
    }
354 355 356
    auto it = std::find_if(o->resources.begin(), o->resources.end(), [resource](const ResourceData &r) { return r.resource == resource; });
    if (it != o->resources.end()) {
        o->resources.erase(it);
357 358 359
    }
}

360
void OutputInterface::Private::sendMode(wl_resource *resource, const Mode &mode)
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
{
    int32_t flags = 0;
    if (mode.flags.testFlag(ModeFlag::Current)) {
        flags |= WL_OUTPUT_MODE_CURRENT;
    }
    if (mode.flags.testFlag(ModeFlag::Preferred)) {
        flags |= WL_OUTPUT_MODE_PREFERRED;
    }
    wl_output_send_mode(resource,
                        flags,
                        mode.size.width(),
                        mode.size.height(),
                        mode.refreshRate);

}

377
void OutputInterface::Private::sendGeometry(wl_resource *resource)
378 379
{
    wl_output_send_geometry(resource,
380 381 382 383
                            globalPosition.x(),
                            globalPosition.y(),
                            physicalSize.width(),
                            physicalSize.height(),
384
                            toSubPixel(),
385 386
                            qPrintable(manufacturer),
                            qPrintable(model),
387 388 389
                            toTransform());
}

390
void OutputInterface::Private::sendScale(const ResourceData &data)
391 392 393 394
{
    if (data.version < 2) {
        return;
    }
395
    wl_output_send_scale(data.resource, scale);
396 397
}

398
void OutputInterface::Private::sendDone(const ResourceData &data)
399 400 401 402 403 404 405
{
    if (data.version < 2) {
        return;
    }
    wl_output_send_done(data.resource);
}

406
void OutputInterface::Private::updateGeometry()
407
{
408
    for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) {
409 410 411 412 413
        sendGeometry((*it).resource);
        sendDone(*it);
    }
}

414
void OutputInterface::Private::updateScale()
415
{
416
    for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) {
417 418 419 420 421 422 423 424
        sendScale(*it);
        sendDone(*it);
    }
}

#define SETTER(setterName, type, argumentName) \
    void OutputInterface::setterName(type arg) \
    { \
425
        Q_D(); \
426
        if (d->argumentName == arg) { \
427 428
            return; \
        } \
429 430
        d->argumentName = arg; \
        emit argumentName##Changed(d->argumentName); \
431 432 433 434 435 436 437 438 439 440 441 442
    }

SETTER(setPhysicalSize, const QSize&, physicalSize)
SETTER(setGlobalPosition, const QPoint&, globalPosition)
SETTER(setManufacturer, const QString&, manufacturer)
SETTER(setModel, const QString&, model)
SETTER(setScale, int, scale)
SETTER(setSubPixel, SubPixel, subPixel)
SETTER(setTransform, Transform, transform)

#undef SETTER

443 444
QSize OutputInterface::physicalSize() const
{
445
    Q_D();
446 447 448 449 450
    return d->physicalSize;
}

QPoint OutputInterface::globalPosition() const
{
451
    Q_D();
452 453 454 455 456
    return d->globalPosition;
}

QString OutputInterface::manufacturer() const
{
457
    Q_D();
458 459 460 461 462
    return d->manufacturer;
}

QString OutputInterface::model() const
{
463
    Q_D();
464 465 466 467 468
    return d->model;
}

int OutputInterface::scale() const
{
469
    Q_D();
470 471 472 473 474
    return d->scale;
}

OutputInterface::SubPixel OutputInterface::subPixel() const
{
475
    Q_D();
476 477 478 479 480
    return d->subPixel;
}

OutputInterface::Transform OutputInterface::transform() const
{
481
    Q_D();
482 483 484 485 486
    return d->transform;
}

QList< OutputInterface::Mode > OutputInterface::modes() const
{
487
    Q_D();
488 489 490
    return d->modes;
}

491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
void OutputInterface::setDpmsMode(OutputInterface::DpmsMode mode)
{
    Q_D();
    if (d->dpms.mode == mode) {
        return;
    }
    d->dpms.mode = mode;
    emit dpmsModeChanged();
}

void OutputInterface::setDpmsSupported(bool supported)
{
    Q_D();
    if (d->dpms.supported == supported) {
        return;
    }
    d->dpms.supported = supported;
    emit dpmsSupportedChanged();
}

OutputInterface::DpmsMode OutputInterface::dpmsMode() const
{
    Q_D();
    return d->dpms.mode;
}

bool OutputInterface::isDpmsSupported() const
{
    Q_D();
    return d->dpms.supported;
}

523 524 525 526 527 528 529 530 531 532 533 534
QVector<wl_resource *> OutputInterface::clientResources(ClientConnection *client) const
{
    Q_D();
    QVector<wl_resource *> ret;
    for (auto it = d->resources.constBegin(), end = d->resources.constEnd(); it != end; ++it) {
        if (wl_resource_get_client((*it).resource) == client->client()) {
            ret << (*it).resource;
        }
    }
    return ret;
}

535 536 537 538 539
OutputInterface *OutputInterface::get(wl_resource* native)
{
    return Private::get(native);
}

540
OutputInterface::Private *OutputInterface::d_func() const
541
{
542
    return reinterpret_cast<Private*>(d.data());
543 544
}

545 546
}
}