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