Changeset View
Changeset View
Standalone View
Standalone View
backends/drm/drm_backend.cpp
Show All 12 Lines | |||||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | 15 | GNU General Public License for more details. | ||
16 | 16 | | |||
17 | You should have received a copy of the GNU General Public License | 17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. | 18 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | *********************************************************************/ | 19 | *********************************************************************/ | ||
20 | #include "drm_backend.h" | 20 | #include "drm_backend.h" | ||
21 | #include "drm_output.h" | ||||
21 | #include "composite.h" | 22 | #include "composite.h" | ||
22 | #include "cursor.h" | 23 | #include "cursor.h" | ||
23 | #include "logging.h" | 24 | #include "logging.h" | ||
24 | #include "logind.h" | 25 | #include "logind.h" | ||
25 | #include "scene_qpainter_drm_backend.h" | 26 | #include "scene_qpainter_drm_backend.h" | ||
26 | #include "screens_drm.h" | 27 | #include "screens_drm.h" | ||
27 | #include "udev.h" | 28 | #include "udev.h" | ||
28 | #include "virtual_terminal.h" | 29 | #include "virtual_terminal.h" | ||
29 | #include "wayland_server.h" | 30 | #include "wayland_server.h" | ||
30 | #if HAVE_GBM | 31 | #if HAVE_GBM | ||
31 | #include "egl_gbm_backend.h" | 32 | #include "egl_gbm_backend.h" | ||
32 | #endif | 33 | #endif | ||
33 | // KWayland | 34 | // KWayland | ||
34 | #include <KWayland/Server/display.h> | | |||
35 | #include <KWayland/Server/output_interface.h> | | |||
36 | #include <KWayland/Server/outputchangeset.h> | | |||
37 | #include <KWayland/Server/outputdevice_interface.h> | | |||
38 | #include <KWayland/Server/outputmanagement_interface.h> | | |||
39 | #include <KWayland/Server/outputconfiguration_interface.h> | 35 | #include <KWayland/Server/outputconfiguration_interface.h> | ||
40 | // KF5 | 36 | // KF5 | ||
41 | #include <KConfigGroup> | 37 | #include <KConfigGroup> | ||
42 | #include <KLocalizedString> | 38 | #include <KLocalizedString> | ||
43 | #include <KSharedConfig> | 39 | #include <KSharedConfig> | ||
44 | // Qt | 40 | // Qt | ||
45 | #include <QCryptographicHash> | 41 | #include <QCryptographicHash> | ||
46 | #include <QSocketNotifier> | 42 | #include <QSocketNotifier> | ||
47 | #include <QPainter> | 43 | #include <QPainter> | ||
48 | // system | 44 | // system | ||
49 | #include <unistd.h> | 45 | #include <unistd.h> | ||
50 | #include <sys/mman.h> | | |||
51 | // drm | 46 | // drm | ||
52 | #include <xf86drm.h> | 47 | #include <xf86drm.h> | ||
53 | #include <xf86drmMode.h> | 48 | #include <xf86drmMode.h> | ||
54 | #include <libdrm/drm_mode.h> | 49 | #include <libdrm/drm_mode.h> | ||
55 | #if HAVE_GBM | | |||
56 | #include <gbm.h> | | |||
57 | #endif | | |||
58 | 50 | | |||
graesslin: please remove if no longer needed | |||||
59 | #ifndef DRM_CAP_CURSOR_WIDTH | 51 | #ifndef DRM_CAP_CURSOR_WIDTH | ||
60 | #define DRM_CAP_CURSOR_WIDTH 0x8 | 52 | #define DRM_CAP_CURSOR_WIDTH 0x8 | ||
61 | #endif | 53 | #endif | ||
62 | 54 | | |||
63 | #ifndef DRM_CAP_CURSOR_HEIGHT | 55 | #ifndef DRM_CAP_CURSOR_HEIGHT | ||
64 | #define DRM_CAP_CURSOR_HEIGHT 0x9 | 56 | #define DRM_CAP_CURSOR_HEIGHT 0x9 | ||
65 | #endif | 57 | #endif | ||
66 | 58 | | |||
67 | namespace KWin | 59 | namespace KWin | ||
68 | { | 60 | { | ||
69 | 61 | | |||
70 | DpmsInputEventFilter::DpmsInputEventFilter(DrmBackend *backend) | | |||
71 | : InputEventFilter() | | |||
72 | , m_backend(backend) | | |||
73 | { | | |||
74 | } | | |||
75 | | ||||
76 | DpmsInputEventFilter::~DpmsInputEventFilter() = default; | | |||
77 | | ||||
78 | bool DpmsInputEventFilter::pointerEvent(QMouseEvent *event, quint32 nativeButton) | | |||
79 | { | | |||
80 | Q_UNUSED(event) | | |||
81 | Q_UNUSED(nativeButton) | | |||
82 | notify(); | | |||
83 | return true; | | |||
84 | } | | |||
85 | | ||||
86 | bool DpmsInputEventFilter::wheelEvent(QWheelEvent *event) | | |||
87 | { | | |||
88 | Q_UNUSED(event) | | |||
89 | notify(); | | |||
90 | return true; | | |||
91 | } | | |||
92 | | ||||
93 | bool DpmsInputEventFilter::keyEvent(QKeyEvent *event) | | |||
94 | { | | |||
95 | Q_UNUSED(event) | | |||
96 | notify(); | | |||
97 | return true; | | |||
98 | } | | |||
99 | | ||||
100 | bool DpmsInputEventFilter::touchDown(quint32 id, const QPointF &pos, quint32 time) | | |||
101 | { | | |||
102 | Q_UNUSED(pos) | | |||
103 | Q_UNUSED(time) | | |||
104 | if (m_touchPoints.isEmpty()) { | | |||
105 | if (!m_doubleTapTimer.isValid()) { | | |||
106 | // this is the first tap | | |||
107 | m_doubleTapTimer.start(); | | |||
108 | } else { | | |||
109 | if (m_doubleTapTimer.elapsed() < qApp->doubleClickInterval()) { | | |||
110 | m_secondTap = true; | | |||
111 | } else { | | |||
112 | // took too long. Let's consider it a new click | | |||
113 | m_doubleTapTimer.restart(); | | |||
114 | } | | |||
115 | } | | |||
116 | } else { | | |||
117 | // not a double tap | | |||
118 | m_doubleTapTimer.invalidate(); | | |||
119 | m_secondTap = false; | | |||
120 | } | | |||
121 | m_touchPoints << id; | | |||
122 | return true; | | |||
123 | } | | |||
124 | | ||||
125 | bool DpmsInputEventFilter::touchUp(quint32 id, quint32 time) | | |||
126 | { | | |||
127 | Q_UNUSED(time) | | |||
128 | m_touchPoints.removeAll(id); | | |||
129 | if (m_touchPoints.isEmpty() && m_doubleTapTimer.isValid() && m_secondTap) { | | |||
130 | if (m_doubleTapTimer.elapsed() < qApp->doubleClickInterval()) { | | |||
131 | notify(); | | |||
132 | } | | |||
133 | m_doubleTapTimer.invalidate(); | | |||
134 | m_secondTap = false; | | |||
135 | } | | |||
136 | return true; | | |||
137 | } | | |||
138 | | ||||
139 | bool DpmsInputEventFilter::touchMotion(quint32 id, const QPointF &pos, quint32 time) | | |||
140 | { | | |||
141 | Q_UNUSED(id) | | |||
142 | Q_UNUSED(pos) | | |||
143 | Q_UNUSED(time) | | |||
144 | // ignore the event | | |||
145 | return true; | | |||
146 | } | | |||
147 | | ||||
148 | void DpmsInputEventFilter::notify() | | |||
149 | { | | |||
150 | // queued to not modify the list of event filters while filtering | | |||
151 | QMetaObject::invokeMethod(m_backend, "turnOutputsOn", Qt::QueuedConnection); | | |||
152 | } | | |||
153 | | ||||
154 | DrmBackend::DrmBackend(QObject *parent) | 62 | DrmBackend::DrmBackend(QObject *parent) | ||
155 | : AbstractBackend(parent) | 63 | : AbstractBackend(parent) | ||
156 | , m_udev(new Udev) | 64 | , m_udev(new Udev) | ||
157 | , m_udevMonitor(m_udev->monitor()) | 65 | , m_udevMonitor(m_udev->monitor()) | ||
158 | , m_dpmsFilter() | 66 | , m_dpmsFilter() | ||
159 | { | 67 | { | ||
160 | handleOutputs(); | 68 | handleOutputs(); | ||
161 | m_cursor[0] = nullptr; | 69 | m_cursor[0] = nullptr; | ||
▲ Show 20 Lines • Show All 523 Lines • ▼ Show 20 Line(s) | 590 | { | |||
685 | } | 593 | } | ||
686 | bool enabled = false; | 594 | bool enabled = false; | ||
687 | for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { | 595 | for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { | ||
688 | enabled = enabled || (*it)->isDpmsEnabled(); | 596 | enabled = enabled || (*it)->isDpmsEnabled(); | ||
689 | } | 597 | } | ||
690 | setOutputsEnabled(enabled); | 598 | setOutputsEnabled(enabled); | ||
691 | } | 599 | } | ||
692 | 600 | | |||
693 | DrmOutput::DrmOutput(DrmBackend *backend) | | |||
694 | : QObject() | | |||
695 | , m_backend(backend) | | |||
696 | { | | |||
697 | } | | |||
698 | | ||||
699 | DrmOutput::~DrmOutput() | | |||
700 | { | | |||
701 | hideCursor(); | | |||
702 | cleanupBlackBuffer(); | | |||
703 | delete m_waylandOutput.data(); | | |||
704 | delete m_waylandOutputDevice.data(); | | |||
705 | } | | |||
706 | | ||||
707 | void DrmOutput::hideCursor() | | |||
708 | { | | |||
709 | drmModeSetCursor(m_backend->fd(), m_crtcId, 0, 0, 0); | | |||
710 | } | | |||
711 | | ||||
712 | void DrmOutput::restoreSaved() | | |||
713 | { | | |||
714 | if (!m_savedCrtc.isNull()) { | | |||
715 | drmModeSetCrtc(m_backend->fd(), m_savedCrtc->crtc_id, m_savedCrtc->buffer_id, | | |||
716 | m_savedCrtc->x, m_savedCrtc->y, &m_connector, 1, &m_savedCrtc->mode); | | |||
717 | } | | |||
718 | } | | |||
719 | | ||||
720 | void DrmOutput::showCursor(DrmBuffer *c) | | |||
721 | { | | |||
722 | const QSize &s = c->size(); | | |||
723 | drmModeSetCursor(m_backend->fd(), m_crtcId, c->handle(), s.width(), s.height()); | | |||
724 | } | | |||
725 | | ||||
726 | void DrmOutput::moveCursor(const QPoint &globalPos) | | |||
727 | { | | |||
728 | const QPoint p = globalPos - m_globalPos; | | |||
729 | drmModeMoveCursor(m_backend->fd(), m_crtcId, p.x(), p.y()); | | |||
730 | } | | |||
731 | | ||||
732 | QSize DrmOutput::size() const | | |||
733 | { | | |||
734 | return QSize(m_mode.hdisplay, m_mode.vdisplay); | | |||
735 | } | | |||
736 | | ||||
737 | QRect DrmOutput::geometry() const | | |||
738 | { | | |||
739 | return QRect(m_globalPos, size()); | | |||
740 | } | | |||
741 | | ||||
742 | bool DrmOutput::present(DrmBuffer *buffer) | | |||
743 | { | | |||
744 | if (!buffer || buffer->bufferId() == 0) { | | |||
745 | return false; | | |||
746 | } | | |||
747 | if (!VirtualTerminal::self()->isActive()) { | | |||
748 | m_currentBuffer = buffer; | | |||
749 | return false; | | |||
750 | } | | |||
751 | if (m_dpmsMode != DpmsMode::On) { | | |||
752 | return false; | | |||
753 | } | | |||
754 | if (m_currentBuffer) { | | |||
755 | return false; | | |||
756 | } | | |||
757 | if (m_lastStride != buffer->stride() || m_lastGbm != buffer->isGbm()) { | | |||
758 | // need to set a new mode first | | |||
759 | if (!setMode(buffer)) { | | |||
760 | return false; | | |||
761 | } | | |||
762 | } | | |||
763 | const bool ok = drmModePageFlip(m_backend->fd(), m_crtcId, buffer->bufferId(), DRM_MODE_PAGE_FLIP_EVENT, this) == 0; | | |||
764 | if (ok) { | | |||
765 | m_currentBuffer = buffer; | | |||
766 | } else { | | |||
767 | qCWarning(KWIN_DRM) << "Page flip failed"; | | |||
768 | buffer->releaseGbm(); | | |||
769 | } | | |||
770 | return ok; | | |||
771 | } | | |||
772 | | ||||
773 | void DrmOutput::pageFlipped() | | |||
774 | { | | |||
775 | if (!m_currentBuffer) { | | |||
776 | return; | | |||
777 | } | | |||
778 | m_currentBuffer->releaseGbm(); | | |||
779 | m_currentBuffer = nullptr; | | |||
780 | cleanupBlackBuffer(); | | |||
781 | } | | |||
782 | | ||||
783 | void DrmOutput::cleanupBlackBuffer() | | |||
784 | { | | |||
785 | if (m_blackBuffer) { | | |||
786 | delete m_blackBuffer; | | |||
787 | m_blackBuffer = nullptr; | | |||
788 | } | | |||
789 | } | | |||
790 | | ||||
791 | static KWayland::Server::OutputInterface::DpmsMode toWaylandDpmsMode(DrmOutput::DpmsMode mode) | | |||
792 | { | | |||
793 | using namespace KWayland::Server; | | |||
794 | switch (mode) { | | |||
795 | case DrmOutput::DpmsMode::On: | | |||
796 | return OutputInterface::DpmsMode::On; | | |||
797 | case DrmOutput::DpmsMode::Standby: | | |||
798 | return OutputInterface::DpmsMode::Standby; | | |||
799 | case DrmOutput::DpmsMode::Suspend: | | |||
800 | return OutputInterface::DpmsMode::Suspend; | | |||
801 | case DrmOutput::DpmsMode::Off: | | |||
802 | return OutputInterface::DpmsMode::Off; | | |||
803 | default: | | |||
804 | Q_UNREACHABLE(); | | |||
805 | } | | |||
806 | } | | |||
807 | | ||||
808 | static DrmOutput::DpmsMode fromWaylandDpmsMode(KWayland::Server::OutputInterface::DpmsMode wlMode) | | |||
809 | { | | |||
810 | using namespace KWayland::Server; | | |||
811 | switch (wlMode) { | | |||
812 | case OutputInterface::DpmsMode::On: | | |||
813 | return DrmOutput::DpmsMode::On; | | |||
814 | case OutputInterface::DpmsMode::Standby: | | |||
815 | return DrmOutput::DpmsMode::Standby; | | |||
816 | case OutputInterface::DpmsMode::Suspend: | | |||
817 | return DrmOutput::DpmsMode::Suspend; | | |||
818 | case OutputInterface::DpmsMode::Off: | | |||
819 | return DrmOutput::DpmsMode::Off; | | |||
820 | default: | | |||
821 | Q_UNREACHABLE(); | | |||
822 | } | | |||
823 | } | | |||
824 | | ||||
825 | void DrmOutput::init(drmModeConnector *connector) | | |||
826 | { | | |||
827 | initEdid(connector); | | |||
828 | initDpms(connector); | | |||
829 | initUuid(); | | |||
830 | m_savedCrtc.reset(drmModeGetCrtc(m_backend->fd(), m_crtcId)); | | |||
831 | blank(); | | |||
832 | setDpms(DpmsMode::On); | | |||
833 | if (!m_waylandOutput.isNull()) { | | |||
834 | delete m_waylandOutput.data(); | | |||
835 | m_waylandOutput.clear(); | | |||
836 | } | | |||
837 | m_waylandOutput = waylandServer()->display()->createOutput(); | | |||
838 | if (!m_waylandOutputDevice.isNull()) { | | |||
839 | delete m_waylandOutputDevice.data(); | | |||
840 | m_waylandOutputDevice.clear(); | | |||
841 | } | | |||
842 | m_waylandOutputDevice = waylandServer()->display()->createOutputDevice(); | | |||
843 | m_waylandOutputDevice->setUuid(m_uuid); | | |||
844 | | ||||
845 | if (!m_edid.eisaId.isEmpty()) { | | |||
846 | m_waylandOutput->setManufacturer(QString::fromLatin1(m_edid.eisaId)); | | |||
847 | } else { | | |||
848 | m_waylandOutput->setManufacturer(i18n("unknown")); | | |||
849 | } | | |||
850 | m_waylandOutputDevice->setManufacturer(m_waylandOutput->manufacturer()); | | |||
851 | | ||||
852 | if (!m_edid.monitorName.isEmpty()) { | | |||
853 | QString model = QString::fromLatin1(m_edid.monitorName); | | |||
854 | if (!m_edid.serialNumber.isEmpty()) { | | |||
855 | model.append('/'); | | |||
856 | model.append(QString::fromLatin1(m_edid.serialNumber)); | | |||
857 | } | | |||
858 | m_waylandOutput->setModel(model); | | |||
859 | } else if (!m_edid.serialNumber.isEmpty()) { | | |||
860 | m_waylandOutput->setModel(QString::fromLatin1(m_edid.serialNumber)); | | |||
861 | } else { | | |||
862 | m_waylandOutput->setModel(i18n("unknown")); | | |||
863 | } | | |||
864 | m_waylandOutputDevice->setModel(m_waylandOutput->model()); | | |||
865 | | ||||
866 | QSize physicalSize = !m_edid.physicalSize.isEmpty() ? m_edid.physicalSize : QSize(connector->mmWidth, connector->mmHeight); | | |||
867 | // the size might be completely borked. E.g. Samsung SyncMaster 2494HS reports 160x90 while in truth it's 520x292 | | |||
868 | // as this information is used to calculate DPI info, it's going to result in everything being huge | | |||
869 | const QByteArray unknown = QByteArrayLiteral("unkown"); | | |||
870 | KConfigGroup group = kwinApp()->config()->group("EdidOverwrite").group(m_edid.eisaId.isEmpty() ? unknown : m_edid.eisaId) | | |||
871 | .group(m_edid.monitorName.isEmpty() ? unknown : m_edid.monitorName) | | |||
872 | .group(m_edid.serialNumber.isEmpty() ? unknown : m_edid.serialNumber); | | |||
873 | if (group.hasKey("PhysicalSize")) { | | |||
874 | const QSize overwriteSize = group.readEntry("PhysicalSize", physicalSize); | | |||
875 | qCWarning(KWIN_DRM) << "Overwriting monitor physical size for" << m_edid.eisaId << "/" << m_edid.monitorName << "/" << m_edid.serialNumber << " from " << physicalSize << "to " << overwriteSize; | | |||
876 | physicalSize = overwriteSize; | | |||
877 | } | | |||
878 | m_waylandOutput->setPhysicalSize(physicalSize); | | |||
879 | m_waylandOutputDevice->setPhysicalSize(physicalSize); | | |||
880 | | ||||
881 | // read in mode information | | |||
882 | for (int i = 0; i < connector->count_modes; ++i) { | | |||
883 | auto *m = &connector->modes[i]; | | |||
884 | KWayland::Server::OutputInterface::ModeFlags flags; | | |||
885 | KWayland::Server::OutputDeviceInterface::ModeFlags deviceflags; | | |||
886 | if (isCurrentMode(m)) { | | |||
887 | flags |= KWayland::Server::OutputInterface::ModeFlag::Current; | | |||
888 | deviceflags |= KWayland::Server::OutputDeviceInterface::ModeFlag::Current; | | |||
889 | } | | |||
890 | if (m->type & DRM_MODE_TYPE_PREFERRED) { | | |||
891 | flags |= KWayland::Server::OutputInterface::ModeFlag::Preferred; | | |||
892 | deviceflags |= KWayland::Server::OutputDeviceInterface::ModeFlag::Preferred; | | |||
893 | } | | |||
894 | | ||||
895 | // Calculate higher precision (mHz) refresh rate | | |||
896 | // logic based on Weston, see compositor-drm.c | | |||
897 | quint64 refreshRate = (m->clock * 1000000LL / m->htotal + m->vtotal / 2) / m->vtotal; | | |||
898 | if (m->flags & DRM_MODE_FLAG_INTERLACE) { | | |||
899 | refreshRate *= 2; | | |||
900 | } | | |||
901 | if (m->flags & DRM_MODE_FLAG_DBLSCAN) { | | |||
902 | refreshRate /= 2; | | |||
903 | } | | |||
904 | if (m->vscan > 1) { | | |||
905 | refreshRate /= m->vscan; | | |||
906 | } | | |||
907 | m_waylandOutput->addMode(QSize(m->hdisplay, m->vdisplay), flags, refreshRate); | | |||
908 | | ||||
909 | KWayland::Server::OutputDeviceInterface::Mode mode; | | |||
910 | mode.id = i; | | |||
911 | mode.size = QSize(m->hdisplay, m->vdisplay); | | |||
912 | mode.flags = deviceflags; | | |||
913 | mode.refreshRate = refreshRate; | | |||
914 | qCDebug(KWIN_DRM) << "Adding mode: " << i << mode.size; | | |||
915 | m_waylandOutputDevice->addMode(mode); | | |||
916 | } | | |||
917 | | ||||
918 | // set dpms | | |||
919 | if (!m_dpms.isNull()) { | | |||
920 | m_waylandOutput->setDpmsSupported(true); | | |||
921 | m_waylandOutput->setDpmsMode(toWaylandDpmsMode(m_dpmsMode)); | | |||
922 | connect(m_waylandOutput.data(), &KWayland::Server::OutputInterface::dpmsModeRequested, this, | | |||
923 | [this] (KWayland::Server::OutputInterface::DpmsMode mode) { | | |||
924 | setDpms(fromWaylandDpmsMode(mode)); | | |||
925 | }, Qt::QueuedConnection | | |||
926 | ); | | |||
927 | } | | |||
928 | | ||||
929 | m_waylandOutput->create(); | | |||
930 | qCDebug(KWIN_DRM) << "Created OutputDevice"; | | |||
931 | m_waylandOutputDevice->create(); | | |||
932 | } | | |||
933 | | ||||
934 | void DrmOutput::initUuid() | | |||
935 | { | | |||
936 | QCryptographicHash hash(QCryptographicHash::Md5); | | |||
937 | hash.addData(QByteArray::number(m_connector)); | | |||
938 | hash.addData(m_edid.eisaId); | | |||
939 | hash.addData(m_edid.monitorName); | | |||
940 | hash.addData(m_edid.serialNumber); | | |||
941 | m_uuid = hash.result().toHex().left(10); | | |||
942 | } | | |||
943 | | ||||
944 | bool DrmOutput::isCurrentMode(const drmModeModeInfo *mode) const | | |||
945 | { | | |||
946 | return mode->clock == m_mode.clock | | |||
947 | && mode->hdisplay == m_mode.hdisplay | | |||
948 | && mode->hsync_start == m_mode.hsync_start | | |||
949 | && mode->hsync_end == m_mode.hsync_end | | |||
950 | && mode->htotal == m_mode.htotal | | |||
951 | && mode->hskew == m_mode.hskew | | |||
952 | && mode->vdisplay == m_mode.vdisplay | | |||
953 | && mode->vsync_start == m_mode.vsync_start | | |||
954 | && mode->vsync_end == m_mode.vsync_end | | |||
955 | && mode->vtotal == m_mode.vtotal | | |||
956 | && mode->vscan == m_mode.vscan | | |||
957 | && mode->vrefresh == m_mode.vrefresh | | |||
958 | && mode->flags == m_mode.flags | | |||
959 | && mode->type == m_mode.type | | |||
960 | && qstrcmp(mode->name, m_mode.name) == 0; | | |||
961 | } | | |||
962 | | ||||
963 | void DrmOutput::blank() | | |||
964 | { | | |||
965 | if (!m_blackBuffer) { | | |||
966 | m_blackBuffer = m_backend->createBuffer(size()); | | |||
967 | m_blackBuffer->map(); | | |||
968 | m_blackBuffer->image()->fill(Qt::black); | | |||
969 | } | | |||
970 | setMode(m_blackBuffer); | | |||
971 | } | | |||
972 | | ||||
973 | bool DrmOutput::setMode(DrmBuffer *buffer) | | |||
974 | { | | |||
975 | if (drmModeSetCrtc(m_backend->fd(), m_crtcId, buffer->bufferId(), 0, 0, &m_connector, 1, &m_mode) == 0) { | | |||
976 | m_lastStride = buffer->stride(); | | |||
977 | m_lastGbm = buffer->isGbm(); | | |||
978 | return true; | | |||
979 | } else { | | |||
980 | qCWarning(KWIN_DRM) << "Mode setting failed"; | | |||
981 | return false; | | |||
982 | } | | |||
983 | } | | |||
984 | | ||||
985 | static bool verifyEdidHeader(drmModePropertyBlobPtr edid) | | |||
986 | { | | |||
987 | const uint8_t *data = reinterpret_cast<uint8_t*>(edid->data); | | |||
988 | if (data[0] != 0x00) { | | |||
989 | return false; | | |||
990 | } | | |||
991 | for (int i = 1; i < 7; ++i) { | | |||
992 | if (data[i] != 0xFF) { | | |||
993 | return false; | | |||
994 | } | | |||
995 | } | | |||
996 | if (data[7] != 0x00) { | | |||
997 | return false; | | |||
998 | } | | |||
999 | return true; | | |||
1000 | } | | |||
1001 | | ||||
1002 | static QByteArray extractEisaId(drmModePropertyBlobPtr edid) | | |||
1003 | { | | |||
1004 | /* | | |||
1005 | * From EDID standard section 3.4: | | |||
1006 | * The ID Manufacturer Name field, shown in Table 3.5, contains a 2-byte representation of the monitor's | | |||
1007 | * manufacturer. This is the same as the EISA ID. It is based on compressed ASCII, “0001=A” ... “11010=Z”. | | |||
1008 | * | | |||
1009 | * The table: | | |||
1010 | * | Byte | Bit | | | |||
1011 | * | | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | | | |||
1012 | * ---------------------------------------- | | |||
1013 | * | 1 | 0)| (4| 3 | 2 | 1 | 0)| (4| 3 | | | |||
1014 | * | | * | Character 1 | Char 2| | | |||
1015 | * ---------------------------------------- | | |||
1016 | * | 2 | 2 | 1 | 0)| (4| 3 | 2 | 1 | 0)| | | |||
1017 | * | | Character2| Character 3 | | | |||
1018 | * ---------------------------------------- | | |||
1019 | **/ | | |||
1020 | const uint8_t *data = reinterpret_cast<uint8_t*>(edid->data); | | |||
1021 | static const uint offset = 0x8; | | |||
1022 | char id[4]; | | |||
1023 | if (data[offset] >> 7) { | | |||
1024 | // bit at position 7 is not a 0 | | |||
1025 | return QByteArray(); | | |||
1026 | } | | |||
1027 | // shift two bits to right, and with 7 right most bits | | |||
1028 | id[0] = 'A' + ((data[offset] >> 2) & 0x1f) -1; | | |||
1029 | // for first byte: take last two bits and shift them 3 to left (000xx000) | | |||
1030 | // for second byte: shift 5 bits to right and take 3 right most bits (00000xxx) | | |||
1031 | // or both together | | |||
1032 | id[1] = 'A' + (((data[offset] & 0x3) << 3) | ((data[offset + 1] >> 5) & 0x7)) - 1; | | |||
1033 | // take five right most bits | | |||
1034 | id[2] = 'A' + (data[offset + 1] & 0x1f) - 1; | | |||
1035 | id[3] = '\0'; | | |||
1036 | return QByteArray(id); | | |||
1037 | } | | |||
1038 | | ||||
1039 | static void extractMonitorDescriptorDescription(drmModePropertyBlobPtr blob, DrmOutput::Edid &edid) | | |||
1040 | { | | |||
1041 | // see section 3.10.3 | | |||
1042 | const uint8_t *data = reinterpret_cast<uint8_t*>(blob->data); | | |||
1043 | static const uint offset = 0x36; | | |||
1044 | static const uint blockLength = 18; | | |||
1045 | for (int i = 0; i < 5; ++i) { | | |||
1046 | const uint co = offset + i * blockLength; | | |||
1047 | // Flag = 0000h when block used as descriptor | | |||
1048 | if (data[co] != 0) { | | |||
1049 | continue; | | |||
1050 | } | | |||
1051 | if (data[co + 1] != 0) { | | |||
1052 | continue; | | |||
1053 | } | | |||
1054 | // Reserved = 00h when block used as descriptor | | |||
1055 | if (data[co + 2] != 0) { | | |||
1056 | continue; | | |||
1057 | } | | |||
1058 | /* | | |||
1059 | * FFh: Monitor Serial Number - Stored as ASCII, code page # 437, ≤ 13 bytes. | | |||
1060 | * FEh: ASCII String - Stored as ASCII, code page # 437, ≤ 13 bytes. | | |||
1061 | * FDh: Monitor range limits, binary coded | | |||
1062 | * FCh: Monitor name, stored as ASCII, code page # 437 | | |||
1063 | * FBh: Descriptor contains additional color point data | | |||
1064 | * FAh: Descriptor contains additional Standard Timing Identifications | | |||
1065 | * F9h - 11h: Currently undefined | | |||
1066 | * 10h: Dummy descriptor, used to indicate that the descriptor space is unused | | |||
1067 | * 0Fh - 00h: Descriptor defined by manufacturer. | | |||
1068 | */ | | |||
1069 | if (data[co + 3] == 0xfc && edid.monitorName.isEmpty()) { | | |||
1070 | edid.monitorName = QByteArray((const char *)(&data[co + 5]), 12).trimmed(); | | |||
1071 | } | | |||
1072 | if (data[co + 3] == 0xfe) { | | |||
1073 | const QByteArray id = QByteArray((const char *)(&data[co + 5]), 12).trimmed(); | | |||
1074 | if (!id.isEmpty()) { | | |||
1075 | edid.eisaId = id; | | |||
1076 | } | | |||
1077 | } | | |||
1078 | if (data[co + 3] == 0xff) { | | |||
1079 | edid.serialNumber = QByteArray((const char *)(&data[co + 5]), 12).trimmed(); | | |||
1080 | } | | |||
1081 | } | | |||
1082 | } | | |||
1083 | | ||||
1084 | static QByteArray extractSerialNumber(drmModePropertyBlobPtr edid) | | |||
1085 | { | | |||
1086 | // see section 3.4 | | |||
1087 | const uint8_t *data = reinterpret_cast<uint8_t*>(edid->data); | | |||
1088 | static const uint offset = 0x0C; | | |||
1089 | /* | | |||
1090 | * The ID serial number is a 32-bit serial number used to differentiate between individual instances of the same model | | |||
1091 | * of monitor. Its use is optional. When used, the bit order for this field follows that shown in Table 3.6. The EDID | | |||
1092 | * structure Version 1 Revision 1 and later offer a way to represent the serial number of the monitor as an ASCII string | | |||
1093 | * in a separate descriptor block. | | |||
1094 | */ | | |||
1095 | uint32_t serialNumber = 0; | | |||
1096 | serialNumber = (uint32_t) data[offset + 0]; | | |||
1097 | serialNumber |= (uint32_t) data[offset + 1] << 8; | | |||
1098 | serialNumber |= (uint32_t) data[offset + 2] << 16; | | |||
1099 | serialNumber |= (uint32_t) data[offset + 3] << 24; | | |||
1100 | if (serialNumber == 0) { | | |||
1101 | return QByteArray(); | | |||
1102 | } | | |||
1103 | return QByteArray::number(serialNumber); | | |||
1104 | } | | |||
1105 | | ||||
1106 | static QSize extractPhysicalSize(drmModePropertyBlobPtr edid) | | |||
1107 | { | | |||
1108 | const uint8_t *data = reinterpret_cast<uint8_t*>(edid->data); | | |||
1109 | return QSize(data[0x15], data[0x16]) * 10; | | |||
1110 | } | | |||
1111 | | ||||
1112 | void DrmOutput::initEdid(drmModeConnector *connector) | | |||
1113 | { | | |||
1114 | ScopedDrmPointer<_drmModePropertyBlob, &drmModeFreePropertyBlob> edid; | | |||
1115 | for (int i = 0; i < connector->count_props; ++i) { | | |||
1116 | ScopedDrmPointer<_drmModeProperty, &drmModeFreeProperty> property(drmModeGetProperty(m_backend->fd(), connector->props[i])); | | |||
1117 | if (!property) { | | |||
1118 | continue; | | |||
1119 | } | | |||
1120 | if ((property->flags & DRM_MODE_PROP_BLOB) && qstrcmp(property->name, "EDID") == 0) { | | |||
1121 | edid.reset(drmModeGetPropertyBlob(m_backend->fd(), connector->prop_values[i])); | | |||
1122 | } | | |||
1123 | } | | |||
1124 | if (!edid) { | | |||
1125 | return; | | |||
1126 | } | | |||
1127 | | ||||
1128 | // for documentation see: http://read.pudn.com/downloads110/ebook/456020/E-EDID%20Standard.pdf | | |||
1129 | if (edid->length < 128) { | | |||
1130 | return; | | |||
1131 | } | | |||
1132 | if (!verifyEdidHeader(edid.data())) { | | |||
1133 | return; | | |||
1134 | } | | |||
1135 | m_edid.eisaId = extractEisaId(edid.data()); | | |||
1136 | m_edid.serialNumber = extractSerialNumber(edid.data()); | | |||
1137 | | ||||
1138 | // parse monitor descriptor description | | |||
1139 | extractMonitorDescriptorDescription(edid.data(), m_edid); | | |||
1140 | | ||||
1141 | m_edid.physicalSize = extractPhysicalSize(edid.data()); | | |||
1142 | } | | |||
1143 | | ||||
1144 | void DrmOutput::initDpms(drmModeConnector *connector) | | |||
1145 | { | | |||
1146 | for (int i = 0; i < connector->count_props; ++i) { | | |||
1147 | ScopedDrmPointer<_drmModeProperty, &drmModeFreeProperty> property(drmModeGetProperty(m_backend->fd(), connector->props[i])); | | |||
1148 | if (!property) { | | |||
1149 | continue; | | |||
1150 | } | | |||
1151 | if (qstrcmp(property->name, "DPMS") == 0) { | | |||
1152 | m_dpms.swap(property); | | |||
1153 | break; | | |||
1154 | } | | |||
1155 | } | | |||
1156 | } | | |||
1157 | | ||||
1158 | void DrmOutput::setDpms(DrmOutput::DpmsMode mode) | | |||
1159 | { | | |||
1160 | if (m_dpms.isNull()) { | | |||
1161 | return; | | |||
1162 | } | | |||
1163 | if (mode == m_dpmsMode) { | | |||
1164 | return; | | |||
1165 | } | | |||
1166 | if (drmModeConnectorSetProperty(m_backend->fd(), m_connector, m_dpms->prop_id, uint64_t(mode)) != 0) { | | |||
1167 | qCWarning(KWIN_DRM) << "Setting DPMS failed"; | | |||
1168 | return; | | |||
1169 | } | | |||
1170 | m_dpmsMode = mode; | | |||
1171 | if (m_waylandOutput) { | | |||
1172 | m_waylandOutput->setDpmsMode(toWaylandDpmsMode(m_dpmsMode)); | | |||
1173 | } | | |||
1174 | emit dpmsChanged(); | | |||
1175 | if (m_dpmsMode != DpmsMode::On) { | | |||
1176 | m_backend->outputWentOff(); | | |||
1177 | } else { | | |||
1178 | m_backend->checkOutputsAreOn(); | | |||
1179 | blank(); | | |||
1180 | if (Compositor *compositor = Compositor::self()) { | | |||
1181 | compositor->addRepaintFull(); | | |||
1182 | } | | |||
1183 | } | | |||
1184 | } | | |||
1185 | | ||||
1186 | QString DrmOutput::name() const | | |||
1187 | { | | |||
1188 | if (!m_waylandOutput) { | | |||
1189 | return i18n("unknown"); | | |||
1190 | } | | |||
1191 | return QStringLiteral("%1 %2").arg(m_waylandOutput->manufacturer()).arg(m_waylandOutput->model()); | | |||
1192 | } | | |||
1193 | | ||||
1194 | int DrmOutput::currentRefreshRate() const | | |||
1195 | { | | |||
1196 | if (!m_waylandOutput) { | | |||
1197 | return 60000; | | |||
1198 | } | | |||
1199 | return m_waylandOutput->refreshRate(); | | |||
1200 | } | | |||
1201 | | ||||
1202 | void DrmOutput::setGlobalPos(const QPoint &pos) | | |||
1203 | { | | |||
1204 | m_globalPos = pos; | | |||
1205 | if (m_waylandOutput) { | | |||
1206 | m_waylandOutput->setGlobalPosition(pos); | | |||
1207 | } | | |||
1208 | if (m_waylandOutputDevice) { | | |||
1209 | m_waylandOutputDevice->setGlobalPosition(pos); | | |||
1210 | } | | |||
1211 | } | | |||
1212 | | ||||
1213 | void DrmOutput::setChanges(KWayland::Server::OutputChangeSet *changes) | | |||
1214 | { | | |||
1215 | m_changeset = changes; | | |||
1216 | qCDebug(KWIN_DRM) << "set changes in DrmOutput"; | | |||
1217 | commitChanges(); | | |||
1218 | } | | |||
1219 | | ||||
1220 | bool DrmOutput::commitChanges() | | |||
1221 | { | | |||
1222 | Q_ASSERT(!m_waylandOutputDevice.isNull()); | | |||
1223 | Q_ASSERT(!m_waylandOutput.isNull()); | | |||
1224 | | ||||
1225 | if (m_changeset.isNull()) { | | |||
1226 | qCDebug(KWIN_DRM) << "no changes"; | | |||
1227 | // No changes to an output is an entirely valid thing | | |||
1228 | return true; | | |||
1229 | } | | |||
1230 | | ||||
1231 | if (m_changeset->enabledChanged()) { | | |||
1232 | qCDebug(KWIN_DRM) << "Setting enabled:"; | | |||
1233 | m_waylandOutputDevice->setEnabled(m_changeset->enabled()); | | |||
1234 | // FIXME: implement | | |||
1235 | } | | |||
1236 | if (m_changeset->modeChanged()) { | | |||
1237 | qCDebug(KWIN_DRM) << "Setting new mode:" << m_changeset->mode(); | | |||
1238 | m_waylandOutputDevice->setCurrentMode(m_changeset->mode()); | | |||
1239 | // FIXME: implement for wl_output | | |||
1240 | } | | |||
1241 | if (m_changeset->transformChanged()) { | | |||
1242 | qCDebug(KWIN_DRM) << "Server setting transform: " << (int)(m_changeset->transform()); | | |||
1243 | m_waylandOutputDevice->setTransform(m_changeset->transform()); | | |||
1244 | // FIXME: implement for wl_output | | |||
1245 | } | | |||
1246 | if (m_changeset->positionChanged()) { | | |||
1247 | qCDebug(KWIN_DRM) << "Server setting position: " << m_changeset->position(); | | |||
1248 | m_waylandOutput->setGlobalPosition(m_changeset->position()); | | |||
1249 | m_waylandOutputDevice->setGlobalPosition(m_changeset->position()); | | |||
1250 | } | | |||
1251 | if (m_changeset->scaleChanged()) { | | |||
1252 | qCDebug(KWIN_DRM) << "Setting scale:" << m_changeset->scale(); | | |||
1253 | m_waylandOutputDevice->setScale(m_changeset->scale()); | | |||
1254 | // FIXME: implement for wl_output | | |||
1255 | } | | |||
1256 | return true; | | |||
1257 | } | | |||
1258 | | ||||
1259 | DrmBuffer::DrmBuffer(DrmBackend *backend, const QSize &size) | | |||
1260 | : m_backend(backend) | | |||
1261 | , m_size(size) | | |||
1262 | { | | |||
1263 | drm_mode_create_dumb createArgs; | | |||
1264 | memset(&createArgs, 0, sizeof createArgs); | | |||
1265 | createArgs.bpp = 32; | | |||
1266 | createArgs.width = size.width(); | | |||
1267 | createArgs.height = size.height(); | | |||
1268 | if (drmIoctl(m_backend->fd(), DRM_IOCTL_MODE_CREATE_DUMB, &createArgs) != 0) { | | |||
1269 | return; | | |||
1270 | } | | |||
1271 | m_handle = createArgs.handle; | | |||
1272 | m_bufferSize = createArgs.size; | | |||
1273 | m_stride = createArgs.pitch; | | |||
1274 | drmModeAddFB(m_backend->fd(), size.width(), size.height(), 24, 32, | | |||
1275 | m_stride, createArgs.handle, &m_bufferId); | | |||
1276 | } | | |||
1277 | | ||||
1278 | | ||||
1279 | #if HAVE_GBM | | |||
1280 | static void gbmCallback(gbm_bo *bo, void *data) | | |||
1281 | { | | |||
1282 | DrmBackend *backend = reinterpret_cast<DrmBackend*>(data); | | |||
1283 | const auto &buffers = backend->buffers(); | | |||
1284 | for (auto buffer: buffers) { | | |||
1285 | if (buffer->gbm() == bo) { | | |||
1286 | delete buffer; | | |||
1287 | return; | | |||
1288 | } | | |||
1289 | } | | |||
1290 | } | | |||
1291 | #endif | | |||
1292 | | ||||
1293 | DrmBuffer::DrmBuffer(DrmBackend *backend, gbm_surface *surface) | | |||
1294 | : m_backend(backend) | | |||
1295 | , m_surface(surface) | | |||
1296 | { | | |||
1297 | #if HAVE_GBM | | |||
1298 | m_bo = gbm_surface_lock_front_buffer(surface); | | |||
1299 | if (!m_bo) { | | |||
1300 | qCWarning(KWIN_DRM) << "Locking front buffer failed"; | | |||
1301 | return; | | |||
1302 | } | | |||
1303 | m_size = QSize(gbm_bo_get_width(m_bo), gbm_bo_get_height(m_bo)); | | |||
1304 | m_stride = gbm_bo_get_stride(m_bo); | | |||
1305 | if (drmModeAddFB(m_backend->fd(), m_size.width(), m_size.height(), 24, 32, m_stride, gbm_bo_get_handle(m_bo).u32, &m_bufferId) != 0) { | | |||
1306 | qCWarning(KWIN_DRM) << "drmModeAddFB failed"; | | |||
1307 | } | | |||
1308 | gbm_bo_set_user_data(m_bo, m_backend, gbmCallback); | | |||
1309 | #endif | | |||
1310 | } | | |||
1311 | | ||||
1312 | DrmBuffer::~DrmBuffer() | | |||
1313 | { | | |||
1314 | m_backend->bufferDestroyed(this); | | |||
1315 | delete m_image; | | |||
1316 | if (m_memory) { | | |||
1317 | munmap(m_memory, m_bufferSize); | | |||
1318 | } | | |||
1319 | if (m_bufferId) { | | |||
1320 | drmModeRmFB(m_backend->fd(), m_bufferId); | | |||
1321 | } | | |||
1322 | if (m_handle) { | | |||
1323 | drm_mode_destroy_dumb destroyArgs; | | |||
1324 | destroyArgs.handle = m_handle; | | |||
1325 | drmIoctl(m_backend->fd(), DRM_IOCTL_MODE_DESTROY_DUMB, &destroyArgs); | | |||
1326 | } | | |||
1327 | releaseGbm(); | | |||
1328 | } | | |||
1329 | | ||||
1330 | bool DrmBuffer::map(QImage::Format format) | | |||
1331 | { | | |||
1332 | if (!m_handle || !m_bufferId) { | | |||
1333 | return false; | | |||
1334 | } | | |||
1335 | drm_mode_map_dumb mapArgs; | | |||
1336 | memset(&mapArgs, 0, sizeof mapArgs); | | |||
1337 | mapArgs.handle = m_handle; | | |||
1338 | if (drmIoctl(m_backend->fd(), DRM_IOCTL_MODE_MAP_DUMB, &mapArgs) != 0) { | | |||
1339 | return false; | | |||
1340 | } | | |||
1341 | void *address = mmap(nullptr, m_bufferSize, PROT_WRITE, MAP_SHARED, m_backend->fd(), mapArgs.offset); | | |||
1342 | if (address == MAP_FAILED) { | | |||
1343 | return false; | | |||
1344 | } | | |||
1345 | m_memory = address; | | |||
1346 | m_image = new QImage((uchar*)m_memory, m_size.width(), m_size.height(), m_stride, format); | | |||
1347 | return !m_image->isNull(); | | |||
1348 | } | | |||
1349 | | ||||
1350 | void DrmBuffer::releaseGbm() | | |||
1351 | { | | |||
1352 | #if HAVE_GBM | | |||
1353 | if (m_bo) { | | |||
1354 | gbm_surface_release_buffer(m_surface, m_bo); | | |||
1355 | m_bo = nullptr; | | |||
1356 | } | | |||
1357 | #endif | | |||
1358 | } | | |||
1359 | 601 | | |||
1360 | } | 602 | } |
please remove if no longer needed