Changeset View
Changeset View
Standalone View
Standalone View
backends/xrandr/xrandroutput.cpp
Show All 18 Lines | |||||
19 | #include "xrandroutput.h" | 19 | #include "xrandroutput.h" | ||
20 | 20 | | |||
21 | #include "config.h" | 21 | #include "config.h" | ||
22 | #include "xrandr.h" | 22 | #include "xrandr.h" | ||
23 | #include "xrandrconfig.h" | 23 | #include "xrandrconfig.h" | ||
24 | #include "xrandrmode.h" | 24 | #include "xrandrmode.h" | ||
25 | #include "../utils.h" | 25 | #include "../utils.h" | ||
26 | 26 | | |||
27 | #include <xcb/render.h> | ||||
28 | | ||||
27 | Q_DECLARE_METATYPE(QList<int>) | 29 | Q_DECLARE_METATYPE(QList<int>) | ||
28 | 30 | | |||
31 | #define DOUBLE_TO_FIXED(d) ((xcb_render_fixed_t) ((d) * 65536)) | ||||
32 | #define FIXED_TO_DOUBLE(f) ((double) ((f) / 65536.0)) | ||||
33 | | ||||
34 | xcb_render_fixed_t fOne = DOUBLE_TO_FIXED(1); | ||||
35 | xcb_render_fixed_t fZero = DOUBLE_TO_FIXED(0); | ||||
36 | | ||||
29 | XRandROutput::XRandROutput(xcb_randr_output_t id, XRandRConfig *config) | 37 | XRandROutput::XRandROutput(xcb_randr_output_t id, XRandRConfig *config) | ||
30 | : QObject(config) | 38 | : QObject(config) | ||
31 | , m_config(config) | 39 | , m_config(config) | ||
32 | , m_id(id) | 40 | , m_id(id) | ||
33 | , m_primary(false) | 41 | , m_primary(false) | ||
34 | , m_type(KScreen::Output::Unknown) | 42 | , m_type(KScreen::Output::Unknown) | ||
43 | , m_replicationSource(XCB_NONE) | ||||
44 | , m_pendingReplicationSource(XCB_NONE) | ||||
35 | , m_crtc(nullptr) | 45 | , m_crtc(nullptr) | ||
36 | { | 46 | { | ||
37 | init(); | 47 | init(); | ||
38 | } | 48 | } | ||
39 | 49 | | |||
40 | XRandROutput::~XRandROutput() | 50 | XRandROutput::~XRandROutput() | ||
41 | { | 51 | { | ||
42 | } | 52 | } | ||
▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Line(s) | |||||
96 | } | 106 | } | ||
97 | 107 | | |||
98 | KScreen::Output::Rotation XRandROutput::rotation() const | 108 | KScreen::Output::Rotation XRandROutput::rotation() const | ||
99 | { | 109 | { | ||
100 | return static_cast<KScreen::Output::Rotation>(m_crtc ? m_crtc->rotation() : | 110 | return static_cast<KScreen::Output::Rotation>(m_crtc ? m_crtc->rotation() : | ||
101 | XCB_RANDR_ROTATION_ROTATE_0); | 111 | XCB_RANDR_ROTATION_ROTATE_0); | ||
102 | } | 112 | } | ||
103 | 113 | | |||
114 | bool XRandROutput::isHorizontal() const | ||||
115 | { | ||||
116 | const auto rot = rotation(); | ||||
117 | return rot == KScreen::Output::Rotation::None || rot == KScreen::Output::Rotation::Inverted; | ||||
118 | } | ||||
119 | | ||||
104 | QByteArray XRandROutput::edid() const | 120 | QByteArray XRandROutput::edid() const | ||
105 | { | 121 | { | ||
106 | if (m_edid.isNull()) { | 122 | if (m_edid.isNull()) { | ||
107 | m_edid = XRandR::outputEdid(m_id); | 123 | m_edid = XRandR::outputEdid(m_id); | ||
108 | } | 124 | } | ||
109 | return m_edid; | 125 | return m_edid; | ||
110 | } | 126 | } | ||
111 | 127 | | |||
112 | XRandRCrtc* XRandROutput::crtc() const | 128 | XRandRCrtc* XRandROutput::crtc() const | ||
113 | { | 129 | { | ||
114 | return m_crtc; | 130 | return m_crtc; | ||
115 | } | 131 | } | ||
116 | 132 | | |||
133 | xcb_randr_output_t XRandROutput::replicationSource() const | ||||
134 | { | ||||
135 | return m_replicationSource; | ||||
136 | } | ||||
137 | | ||||
138 | void XRandROutput::setReplicationSource(xcb_randr_output_t source) | ||||
139 | { | ||||
140 | m_pendingReplicationSource = source; | ||||
141 | } | ||||
142 | | ||||
117 | void XRandROutput::update() | 143 | void XRandROutput::update() | ||
118 | { | 144 | { | ||
119 | init(); | 145 | init(); | ||
120 | } | 146 | } | ||
121 | 147 | | |||
122 | void XRandROutput::update(xcb_randr_crtc_t crtc, xcb_randr_mode_t mode, xcb_randr_connection_t conn, | 148 | void XRandROutput::update(xcb_randr_crtc_t crtc, xcb_randr_mode_t mode, xcb_randr_connection_t conn, | ||
123 | bool primary) | 149 | bool primary) | ||
124 | { | 150 | { | ||
▲ Show 20 Lines • Show All 170 Lines • ▼ Show 20 Line(s) | 294 | { | |||
295 | if (!connectorType) { | 321 | if (!connectorType) { | ||
296 | return type; | 322 | return type; | ||
297 | } | 323 | } | ||
298 | 324 | | |||
299 | type = connectorType; | 325 | type = connectorType; | ||
300 | return type; | 326 | return type; | ||
301 | } | 327 | } | ||
302 | 328 | | |||
329 | bool isScaling(const xcb_render_transform_t &tr) | ||||
330 | { | ||||
331 | return tr.matrix11 != fZero && tr.matrix12 == fZero && tr.matrix13 == fZero && | ||||
332 | tr.matrix21 == fZero && tr.matrix22 != fZero && tr.matrix23 == fZero && | ||||
333 | tr.matrix31 == fZero && tr.matrix32 == fZero && tr.matrix33 == fOne; | ||||
334 | } | ||||
335 | | ||||
336 | xcb_render_transform_t zeroMatrix() | ||||
337 | { | ||||
338 | return { DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), | ||||
339 | DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), | ||||
340 | DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0) }; | ||||
341 | } | ||||
342 | | ||||
343 | xcb_render_transform_t XRandROutput::currentTransform() const | ||||
344 | { | ||||
345 | auto cookie = xcb_randr_get_crtc_transform(XCB::connection(), m_crtc->crtc()); | ||||
346 | xcb_generic_error_t *error = nullptr; | ||||
347 | auto *reply = xcb_randr_get_crtc_transform_reply(XCB::connection(), cookie, &error); | ||||
348 | if (error) { | ||||
349 | return zeroMatrix(); | ||||
350 | } | ||||
351 | | ||||
352 | const xcb_render_transform_t transform = reply->pending_transform; | ||||
353 | free(reply); | ||||
354 | return transform; | ||||
355 | } | ||||
356 | | ||||
357 | QSizeF XRandROutput::scaledSize(xcb_render_transform_t transform) const | ||||
358 | { | ||||
359 | const QSize ownSize = size(); | ||||
360 | if (!ownSize.isValid()) { | ||||
361 | return QSize(); | ||||
362 | } | ||||
363 | | ||||
364 | const qreal width = FIXED_TO_DOUBLE(transform.matrix11) * ownSize.width(); | ||||
365 | const qreal height = FIXED_TO_DOUBLE(transform.matrix22) * ownSize.height(); | ||||
366 | | ||||
367 | return QSizeF(width, height); | ||||
368 | } | ||||
369 | | ||||
370 | bool XRandROutput::isReplicaOf(XRandROutput *output, xcb_render_transform_t ownTransform) const | ||||
371 | { | ||||
372 | if (output->id() == m_id) { | ||||
373 | return false; | ||||
374 | } | ||||
375 | if (output->position() != position()) { | ||||
376 | return false; | ||||
377 | } | ||||
378 | if (output->replicationSource() != XCB_NONE) { | ||||
379 | return false; | ||||
380 | } | ||||
381 | | ||||
382 | const QSizeF sSize = scaledSize(ownTransform); | ||||
383 | if (!sSize.isValid()) { | ||||
384 | return false; | ||||
385 | } | ||||
386 | | ||||
387 | const auto outputTransform = output->currentTransform(); | ||||
388 | if (!isScaling(outputTransform)) { | ||||
389 | return false; | ||||
390 | } | ||||
391 | | ||||
392 | if (sSize != output->scaledSize(outputTransform)) { | ||||
393 | return false; | ||||
394 | } | ||||
395 | | ||||
396 | return true; | ||||
397 | } | ||||
398 | | ||||
399 | xcb_render_transform_t unityTransform() | ||||
400 | { | ||||
401 | return { DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), | ||||
402 | DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), | ||||
403 | DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1) }; | ||||
404 | } | ||||
405 | | ||||
406 | xcb_render_transform_t XRandROutput::getReplicationTransform(XRandROutput *source) | ||||
407 | { | ||||
408 | if (!source) { | ||||
409 | return unityTransform(); | ||||
410 | } | ||||
411 | | ||||
412 | const auto *sourceMode = source->currentMode(); | ||||
413 | const auto *ownMode = currentMode(); | ||||
414 | if (!sourceMode || !ownMode) { | ||||
415 | return unityTransform(); | ||||
416 | } | ||||
417 | | ||||
418 | const QSize sourceSize = sourceMode->size(); | ||||
419 | QSize size = ownMode->size(); | ||||
420 | | ||||
421 | if (isHorizontal() != source->isHorizontal()) { | ||||
422 | size.transpose(); | ||||
423 | } | ||||
424 | | ||||
425 | const qreal widthFactor = sourceSize.width() / (qreal)size.width(); | ||||
426 | const qreal heightFactor = sourceSize.height() / (qreal)size.height(); | ||||
427 | | ||||
428 | xcb_render_transform_t transform = unityTransform(); | ||||
429 | transform.matrix11 = DOUBLE_TO_FIXED(widthFactor); | ||||
430 | transform.matrix22 = DOUBLE_TO_FIXED(heightFactor); | ||||
431 | | ||||
432 | return transform; | ||||
433 | } | ||||
434 | | ||||
435 | bool XRandROutput::updateReplicationSource() | ||||
436 | { | ||||
437 | XRandROutput *source = m_config->output(m_pendingReplicationSource); | ||||
438 | if (source && (!source->isEnabled() || !source->isConnected())) { | ||||
439 | source = nullptr; | ||||
440 | } | ||||
441 | | ||||
442 | xcb_render_transform_t transform = getReplicationTransform(source); | ||||
443 | QByteArray filterName(isScaling(transform) ? "bilinear" : "nearest"); | ||||
444 | | ||||
445 | auto cookie = xcb_randr_set_crtc_transform_checked(XCB::connection(), | ||||
446 | m_crtc->crtc(), | ||||
447 | transform, | ||||
448 | filterName.size(), filterName.data(), | ||||
449 | 0, nullptr); | ||||
450 | xcb_generic_error_t *error = xcb_request_check(XCB::connection(), cookie); | ||||
451 | if (error) { | ||||
452 | qCDebug(KSCREEN_XRANDR) << "Error on replication transformation!"; | ||||
453 | free(error); | ||||
454 | return false; | ||||
455 | } | ||||
456 | free(error); | ||||
457 | m_replicationSource = m_pendingReplicationSource; | ||||
458 | return true; | ||||
459 | } | ||||
460 | | ||||
461 | bool XRandROutput::applyReplicationSource() | ||||
462 | { | ||||
463 | if (!m_crtc) { | ||||
464 | return false; | ||||
465 | } | ||||
466 | if (m_replicationSource == m_pendingReplicationSource) { | ||||
467 | return false; | ||||
468 | } | ||||
469 | return updateReplicationSource(); | ||||
470 | } | ||||
471 | | ||||
303 | KScreen::OutputPtr XRandROutput::toKScreenOutput() const | 472 | KScreen::OutputPtr XRandROutput::toKScreenOutput() const | ||
304 | { | 473 | { | ||
305 | KScreen::OutputPtr kscreenOutput(new KScreen::Output); | 474 | KScreen::OutputPtr kscreenOutput(new KScreen::Output); | ||
306 | 475 | | |||
307 | const bool signalsBlocked = kscreenOutput->signalsBlocked(); | 476 | const bool signalsBlocked = kscreenOutput->signalsBlocked(); | ||
308 | kscreenOutput->blockSignals(true); | 477 | kscreenOutput->blockSignals(true); | ||
309 | kscreenOutput->setId(m_id); | 478 | kscreenOutput->setId(m_id); | ||
310 | kscreenOutput->setType(m_type); | 479 | kscreenOutput->setType(m_type); | ||
Show All 25 Lines | 489 | if (isConnected()) { | |||
336 | }(m_clones)); | 505 | }(m_clones)); | ||
337 | kscreenOutput->setEnabled(isEnabled()); | 506 | kscreenOutput->setEnabled(isEnabled()); | ||
338 | if (isEnabled()) { | 507 | if (isEnabled()) { | ||
339 | kscreenOutput->setSize(size()); | 508 | kscreenOutput->setSize(size()); | ||
340 | kscreenOutput->setPos(position()); | 509 | kscreenOutput->setPos(position()); | ||
341 | kscreenOutput->setRotation(rotation()); | 510 | kscreenOutput->setRotation(rotation()); | ||
342 | kscreenOutput->setCurrentModeId(currentModeId()); | 511 | kscreenOutput->setCurrentModeId(currentModeId()); | ||
343 | } | 512 | } | ||
513 | kscreenOutput->setReplicationSource(m_replicationSource); | ||||
344 | } | 514 | } | ||
345 | 515 | | |||
346 | | ||||
347 | kscreenOutput->blockSignals(signalsBlocked); | 516 | kscreenOutput->blockSignals(signalsBlocked); | ||
348 | return kscreenOutput; | 517 | return kscreenOutput; | ||
349 | } | 518 | } |