Changeset View
Standalone View
Modules/opengl/opengl.cpp
Show All 25 Lines | |||||
26 | #include <QRegExp> | 26 | #include <QRegExp> | ||
27 | #include <QFile> | 27 | #include <QFile> | ||
28 | #include <QTextStream> | 28 | #include <QTextStream> | ||
29 | #include <QProcess> | 29 | #include <QProcess> | ||
30 | #include <QDebug> | 30 | #include <QDebug> | ||
31 | #include <QOpenGLContext> | 31 | #include <QOpenGLContext> | ||
32 | #include <QOffscreenSurface> | 32 | #include <QOffscreenSurface> | ||
33 | #include <QSurfaceFormat> | 33 | #include <QSurfaceFormat> | ||
34 | #include <QDirIterator> | ||||
34 | 35 | | |||
35 | #include <KPluginFactory> | 36 | #include <KPluginFactory> | ||
36 | #include <KPluginLoader> | 37 | #include <KPluginLoader> | ||
37 | 38 | | |||
38 | #include <kaboutdata.h> | 39 | #include <kaboutdata.h> | ||
39 | #include <KLocalizedString> | 40 | #include <KLocalizedString> | ||
40 | #include <kmessagebox.h> | 41 | #include <kmessagebox.h> | ||
41 | 42 | | |||
▲ Show 20 Lines • Show All 109 Lines • ▼ Show 20 Line(s) | 148 | #endif | |||
151 | const char *glExtensions; | 152 | const char *glExtensions; | ||
152 | #if KCM_HAVE_GLX | 153 | #if KCM_HAVE_GLX | ||
153 | const char *gluVersion; | 154 | const char *gluVersion; | ||
154 | const char *gluExtensions; | 155 | const char *gluExtensions; | ||
155 | #endif | 156 | #endif | ||
156 | char *displayName; | 157 | char *displayName; | ||
157 | } gli; | 158 | } gli; | ||
158 | 159 | | |||
159 | static struct { | 160 | struct DriInfo { | ||
160 | QString module, | 161 | QString deviceId; // card0 etc. | ||
161 | pci, | 162 | QString module; | ||
162 | vendor, | 163 | QString pci; | ||
163 | device, | 164 | QString vendor; | ||
164 | subvendor, | 165 | QString device; | ||
165 | rev; | 166 | QString subvendor; | ||
166 | } dri_info; | 167 | QString rev; | ||
168 | | ||||
169 | bool isValid() const | ||||
pino: const | |||||
170 | { | ||||
171 | return (!module.isEmpty() && !pci.isEmpty() && !deviceId.isEmpty()); | ||||
172 | } | ||||
173 | }; | ||||
167 | 174 | | |||
168 | static int ReadPipe(const QString &FileName, QStringList &list) | 175 | static int ReadPipe(const QString &FileName, QStringList &list) | ||
169 | { | 176 | { | ||
170 | QProcess pipe; | 177 | QProcess pipe; | ||
171 | pipe.start(FileName, QIODevice::ReadOnly); | 178 | pipe.start(FileName, QIODevice::ReadOnly); | ||
172 | 179 | | |||
173 | if (!pipe.waitForFinished()) { | 180 | if (!pipe.waitForFinished()) { | ||
174 | // something went wrong, f.e. command not found | 181 | // something went wrong, f.e. command not found | ||
175 | return 0; | 182 | return 0; | ||
176 | } | 183 | } | ||
177 | 184 | | |||
178 | QTextStream t(&pipe); | 185 | QTextStream t(&pipe); | ||
179 | 186 | | |||
180 | while (!t.atEnd()) list.append(t.readLine()); | 187 | while (!t.atEnd()) list.append(t.readLine()); | ||
181 | 188 | | |||
182 | return list.count(); | 189 | return list.count(); | ||
183 | } | 190 | } | ||
184 | 191 | | |||
185 | #if defined(Q_OS_LINUX) | 192 | #if defined(Q_OS_LINUX) | ||
186 | 193 | | |||
187 | static QString get_sysfs_link_name(const QString& path) | 194 | static QString get_sysfs_link_name(const QString &path) | ||
188 | { | 195 | { | ||
189 | const QString target = QFileInfo(path).symLinkTarget(); | 196 | const QString target = QFileInfo(path).symLinkTarget(); | ||
190 | 197 | | |||
191 | const int index = target.lastIndexOf(QChar('/')); | 198 | const int index = target.lastIndexOf(QChar('/')); | ||
192 | if (index == -1) | 199 | if (index == -1) | ||
193 | return QString(); | 200 | return QString(); | ||
194 | 201 | | |||
195 | return target.mid(index + 1); | 202 | return target.mid(index + 1); | ||
196 | } | 203 | } | ||
197 | 204 | | |||
198 | static bool get_drm_device_sysfs() | 205 | static DriInfo get_drm_device_sysfs(const QString &path) | ||
199 | { | 206 | { | ||
200 | struct stat fileInfo; | 207 | DriInfo info; | ||
201 | if (::stat("/dev/dri/card0", &fileInfo) != 0) | 208 | | ||
202 | return false; | 209 | struct stat fileInfo{}; | ||
203 | if ((fileInfo.st_mode & S_IFCHR) != S_IFCHR) | 210 | if (::stat(QFile::encodeName(path), &fileInfo) != 0) { | ||
204 | return false; | 211 | return info; | ||
212 | } | ||||
213 | if ((fileInfo.st_mode & S_IFCHR) != S_IFCHR) { | ||||
214 | return info; | ||||
215 | } | ||||
qPrintable is wrong when passing paths to native C functions; use QFile::encodeName instead pino: qPrintable is wrong when passing paths to native C functions; use QFile::encodeName instead | |||||
Is that also true for paths that are only ascii? Specifically path is a /dev node. sitter: Is that also true for paths that are only ascii? Specifically path is a /dev node. | |||||
sure, i get this specific path is ascii the general recommendation is to use QFile::encodeName for QString -> C paths conversions, no matter what (and viceversa QFile::decodeName for C -> QString); i personally always use these two avoid any possible future mistake pino: sure, i get this specific path is ascii
the general recommendation is to use QFile::encodeName… | |||||
205 | 216 | | |||
206 | const uint16_t devMajor = major(fileInfo.st_rdev); | 217 | const uint16_t devMajor = major(fileInfo.st_rdev); | ||
207 | const uint16_t devMinor = minor(fileInfo.st_rdev); | 218 | const uint16_t devMinor = minor(fileInfo.st_rdev); | ||
208 | QString sysPath = QStringLiteral("/sys/dev/char/%1:%2/device").arg(devMajor).arg(devMinor); | 219 | const QString sysPath = QStringLiteral("/sys/dev/char/%1:%2").arg(devMajor).arg(devMinor); | ||
209 | 220 | | |||
210 | dri_info.pci = get_sysfs_link_name(sysPath); | 221 | info.pci = get_sysfs_link_name(sysPath + QStringLiteral("/device")); | ||
211 | dri_info.module = get_sysfs_link_name(sysPath + QStringLiteral("/driver")); | 222 | info.module = get_sysfs_link_name(sysPath + QStringLiteral("/device/driver")); | ||
212 | 223 | | |||
213 | return (dri_info.pci.size() && dri_info.module.size()); | 224 | return info; | ||
214 | } | 225 | } | ||
215 | 226 | | |||
216 | #define INFO_DRI QStringLiteral("/proc/dri/0/name") | 227 | static QVector<DriInfo> get_drm_devices_sysfs() | ||
217 | | ||||
218 | static bool get_dri_device_proc() | | |||
219 | { | 228 | { | ||
220 | QFile file; | 229 | QVector<DriInfo> list; | ||
221 | file.setFileName(INFO_DRI); | 230 | QDirIterator it("/dev/dri/", {"card*"}, QDir::System); | ||
222 | if (!file.exists() || !file.open(QIODevice::ReadOnly)) | 231 | while (it.hasNext()) { | ||
223 | return false; | 232 | it.next(); | ||
224 | 233 | auto info = get_drm_device_sysfs(it.filePath()); | |||
why not just use QDirIterator instead, so it combines dir listing and filtering? pino: why not just use QDirIterator instead, so it combines dir listing and filtering? | |||||
Hadn't even occured to me. It's a good thought though, I'll get on it. sitter: Hadn't even occured to me. It's a good thought though, I'll get on it. | |||||
225 | QTextStream stream(&file); | 234 | info.deviceId = it.fileName(); | ||
226 | QString line = stream.readLine(); | 235 | if (info.isValid()) { | ||
227 | if (line.isEmpty()) | 236 | list << info; | ||
228 | return false; | 237 | } | ||
229 | dri_info.module = line.mid(0, line.indexOf(0x20)); | 238 | } | ||
230 | 239 | std::sort(list.begin(), list.end(), [](const DriInfo &i1, const DriInfo &i2) -> bool { | |||
231 | // possible formats, for regression testing | 240 | // reverse card0 < card1 < card2 | ||
232 | // line = " PCI:01:00:0"; | 241 | return QString::localeAwareCompare(i1.deviceId, i2.deviceId) < 0; | ||
233 | // line = " pci:0000:01:00.0" | 242 | }); | ||
234 | QRegExp rx = QRegExp("\\b[Pp][Cc][Ii][:]([0-9a-fA-F]+[:])?([0-9a-fA-F]+[:][0-9a-fA-F]+[:.][0-9a-fA-F]+)\\b"); | 243 | return list; | ||
235 | if (rx.indexIn(line)>0) { | | |||
236 | dri_info.pci = rx.cap(2); | | |||
237 | int end = dri_info.pci.lastIndexOf(':'); | | |||
238 | int end2 = dri_info.pci.lastIndexOf('.'); | | |||
239 | if (end2>end) end=end2; | | |||
240 | dri_info.pci[end]='.'; | | |||
241 | return true; | | |||
242 | } | | |||
243 | return false; | | |||
244 | } | 244 | } | ||
245 | 245 | | |||
246 | static bool get_dri_device() | 246 | static QVector<DriInfo> get_dri_devices() | ||
247 | { | 247 | { | ||
apol: I'd make it QVector, less to worry in the future. | |||||
248 | if (!get_drm_device_sysfs() && !get_dri_device_proc()) | 248 | auto infos = get_drm_devices_sysfs(); | ||
249 | return false; | 249 | for (auto it = infos.begin(); it != infos.end(); ++it) { | ||
apol: Probably only want to list files? | |||||
Dirs actually, e.g. /proc/dri/0/name sitter: Dirs actually, e.g. /proc/dri/0/name
I honestly do not know what they are so I figured best to… | |||||
As it turns out the /proc/dri stuff was supposedly removed here https://github.com/torvalds/linux/commit/cb6458f97b53d7f73043206c18014b3ca63ac345 making this code entirely irrelevant since kernel 3.12. sitter: As it turns out the /proc/dri stuff was supposedly removed here https://github. | |||||
apol: Go for it. | |||||
sitter: Already is in latest diff ;) | |||||
250 | 250 | const QString cmd = QStringLiteral("lspci -m -v -s ") + it->pci; | |||
251 | QString cmd = QStringLiteral("lspci -m -v -s ") + dri_info.pci; | | |||
252 | QStringList pci_info; | 251 | QStringList pci_info; | ||
253 | int num; | 252 | int num = 0; | ||
254 | if (((num = ReadPipe(cmd, pci_info)) || | 253 | if (((num = ReadPipe(cmd, pci_info)) || | ||
255 | (num = ReadPipe("/sbin/"+cmd, pci_info)) || | 254 | (num = ReadPipe("/sbin/"+cmd, pci_info)) || | ||
256 | (num = ReadPipe("/usr/sbin/"+cmd, pci_info)) || | 255 | (num = ReadPipe("/usr/sbin/"+cmd, pci_info)) || | ||
257 | (num = ReadPipe("/usr/local/sbin/"+cmd, pci_info))) && num>=7) { | 256 | (num = ReadPipe("/usr/local/sbin/"+cmd, pci_info))) && num>=7) { | ||
258 | QString line; | 257 | QString line; | ||
259 | for (int i=2; i<=6; i++) { | 258 | for (int i=2; i<=6; i++) { | ||
260 | line = pci_info[i]; | 259 | line = pci_info[i]; | ||
261 | line.remove(QRegExp("[^:]*:[ ]*")); | 260 | line.remove(QRegExp("[^:]*:[ ]*")); | ||
262 | switch (i){ | 261 | switch (i) { | ||
263 | case 2: dri_info.vendor = line; break; | 262 | case 2: it->vendor = line; break; | ||
264 | case 3: dri_info.device = line; break; | 263 | case 3: it->device = line; break; | ||
265 | case 4: dri_info.subvendor = line; break; | 264 | case 4: it->subvendor = line; break; | ||
266 | case 6: dri_info.rev = line; break; | 265 | case 6: it->rev = line; break; | ||
267 | } | 266 | } | ||
268 | } | 267 | } | ||
269 | return true; | 268 | } else { | ||
269 | qDebug() << "failed to pcinfo" << cmd;; | ||||
270 | it = infos.erase(it); | ||||
271 | if (it == infos.end()) { | ||||
apol: Use QStandardPaths::findExecutable? | |||||
sitter: I agree. Not really related though. | |||||
272 | break; | ||||
273 | } | ||||
270 | } | 274 | } | ||
271 | 275 | } | |||
272 | return false; | 276 | return infos; | ||
273 | } | 277 | } | ||
274 | 278 | | |||
275 | #elif defined(Q_OS_FREEBSD) | 279 | #elif defined(Q_OS_FREEBSD) | ||
276 | 280 | | |||
277 | static bool get_dri_device() { | 281 | static QVector<DriInfo> get_dri_devices() { | ||
278 | 282 | QVector<DriInfo> list; | |||
279 | QStringList pci_info; | 283 | QStringList pci_info; | ||
280 | if (ReadPipe("sysctl -n hw.dri.0.name",pci_info)) { | 284 | if (ReadPipe("sysctl -n hw.dri.0.name", pci_info)) { | ||
apol: Indentation looks off | |||||
281 | dri_info.module = pci_info[0].mid(0, pci_info[0].indexOf(0x20)); | 285 | DriInfo info; | ||
apol: the {} isn't necessary here. | |||||
286 | info.module = pci_info[0].mid(0, pci_info[0].indexOf(0x20)); | ||||
287 | list << info; | ||||
282 | } | 288 | } | ||
283 | return false; | 289 | return list; | ||
Probably want to list them all on BSD too, right @adridg? apol: Probably want to list them all on BSD too, right @adridg? | |||||
Possibly. I don't have any machines with multiple cards, so I can't tell -- I'd have to ask Niclas about this. I don't know if it's possible for the numbering to start not-at-zero, for instance. In general it might be nice to do this via sysctl(3) , since that saves us pipes and ugliness. But let's leave that for a different time. adridg: Possibly. I don't have any machines with multiple cards, so I can't tell -- I'd have to ask… | |||||
284 | } | 290 | } | ||
285 | 291 | | |||
286 | #else | 292 | #else | ||
287 | 293 | | |||
288 | static bool get_dri_device() { return false; } | 294 | static QVector<DriInfo> get_dri_devices() { return {}; } | ||
apol: return {} | |||||
289 | 295 | | |||
290 | #endif | 296 | #endif | ||
291 | 297 | | |||
292 | #if KCM_HAVE_GLX | 298 | #if KCM_HAVE_GLX | ||
293 | static void | 299 | static void | ||
294 | mesa_hack(Display *dpy, int scrnum) | 300 | mesa_hack(Display *dpy, int scrnum) | ||
295 | { | 301 | { | ||
296 | static const int attribs[] = { | 302 | static const int attribs[] = { | ||
▲ Show 20 Lines • Show All 292 Lines • ▼ Show 20 Line(s) | 583 | #endif | |||
589 | 595 | | |||
590 | } | 596 | } | ||
591 | } | 597 | } | ||
592 | 598 | | |||
593 | } | 599 | } | ||
594 | } | 600 | } | ||
595 | #endif | 601 | #endif | ||
596 | 602 | | |||
603 | static void makeDriItem(const DriInfo &info, QTreeWidgetItem *parent, QTreeWidgetItem *preceding) | ||||
604 | { | ||||
605 | preceding = newItem(parent, preceding, i18n("Vendor"), info.vendor); | ||||
606 | preceding = newItem(parent, preceding, i18n("Device"), info.device); | ||||
607 | preceding = newItem(parent, preceding, i18n("Subvendor"), info.subvendor); | ||||
608 | preceding = newItem(parent, preceding, i18n("Revision"), info.rev); | ||||
609 | preceding = newItem(parent, preceding, i18n("Kernel module"), info.module); | ||||
610 | } | ||||
597 | 611 | | |||
598 | static QTreeWidgetItem *print_screen_info(QTreeWidgetItem *l1, QTreeWidgetItem *after, const QString &title) | 612 | static QTreeWidgetItem *print_screen_info(QTreeWidgetItem *l1, QTreeWidgetItem *after, const QString &title) | ||
599 | { | 613 | { | ||
600 | QTreeWidgetItem *l2 = nullptr, *l3 = nullptr; | 614 | QTreeWidgetItem *l2 = nullptr, *l3 = nullptr; | ||
601 | 615 | | |||
602 | if (after) { | 616 | if (after) { | ||
603 | l1 = newItem(l1, after, title); | 617 | l1 = newItem(l1, after, title); | ||
604 | } else { | 618 | } else { | ||
605 | l1 = newItem(l1, title); | 619 | l1 = newItem(l1, title); | ||
606 | } | 620 | } | ||
607 | 621 | | |||
608 | if (IsDirect) { | 622 | if (IsDirect) { | ||
609 | if (get_dri_device()) { | 623 | const QVector<DriInfo> infos = get_dri_devices(); | ||
624 | if (infos.size() > 0) { | ||||
610 | l2 = newItem(l1, i18n("3D Accelerator")); | 625 | l2 = newItem(l1, i18n("3D Accelerator")); | ||
611 | l2->setExpanded(true); | 626 | l2->setExpanded(true); | ||
612 | l3 = newItem(l2, l3, i18n("Vendor"), dri_info.vendor); | 627 | if (infos.size() > 1) { | ||
613 | l3 = newItem(l2, l3, i18n("Device"), dri_info.device); | 628 | for (const auto &info : infos) { | ||
614 | l3 = newItem(l2, l3, i18n("Subvendor"), dri_info.subvendor); | 629 | l3 = newItem(l2, l3, info.deviceId); | ||
apol: setting twice in a row? I'd skip setting a nullptr. | |||||
Mh. I think that's to ensure l3 (which is passed into newItem) is null on >1 iteration. Indeed garbage though, I'll just pass in nullptr instead. sitter: Mh. I think that's to ensure l3 (which is passed into newItem) is null on >1 iteration. Indeed… | |||||
615 | l3 = newItem(l2, l3, i18n("Revision"), dri_info.rev); | 630 | l3->setExpanded(true); | ||
631 | makeDriItem(info, l3, nullptr); | ||||
632 | } | ||||
633 | } else if (infos.size() == 1) { | ||||
634 | makeDriItem(infos.at(0), l2, nullptr); | ||||
635 | } | ||||
616 | } else { | 636 | } else { | ||
617 | l2 = newItem(l1, l2, i18n("3D Accelerator"), i18n("unknown")); | 637 | l2 = newItem(l1, l2, i18n("3D Accelerator"), i18n("unknown")); | ||
618 | } | 638 | } | ||
619 | } | 639 | } | ||
620 | 640 | | |||
621 | if (l2) { | 641 | if (l2) { | ||
622 | l2 = newItem(l1, l2, i18n("Driver")); | 642 | l2 = newItem(l1, l2, i18n("Driver")); | ||
623 | }else{ | 643 | }else{ | ||
624 | l2 = newItem(l1, i18n("Driver")); | 644 | l2 = newItem(l1, i18n("Driver")); | ||
625 | } | 645 | } | ||
626 | 646 | | |||
627 | l2->setExpanded(true); | 647 | l2->setExpanded(true); | ||
628 | 648 | | |||
629 | l3 = newItem(l2, i18n("Vendor"), gli.glVendor); | 649 | l3 = newItem(l2, i18n("Vendor"), gli.glVendor); | ||
630 | l3 = newItem(l2, l3, i18n("Renderer"), gli.glRenderer); | 650 | l3 = newItem(l2, l3, i18n("Renderer"), gli.glRenderer); | ||
631 | #ifdef KCM_ENABLE_OPENGLES | 651 | #ifdef KCM_ENABLE_OPENGLES | ||
632 | l3 = newItem(l2, l3, i18n("OpenGL ES version"), gli.glVersion); | 652 | l3 = newItem(l2, l3, i18n("OpenGL ES version"), gli.glVersion); | ||
633 | #else | 653 | #else | ||
634 | l3 = newItem(l2, l3, i18n("OpenGL version"), gli.glVersion); | 654 | l3 = newItem(l2, l3, i18n("OpenGL version"), gli.glVersion); | ||
635 | #endif | 655 | #endif | ||
636 | 656 | | |||
637 | if (IsDirect) { | | |||
638 | if (dri_info.module.isEmpty()) dri_info.module = i18n("unknown"); | | |||
639 | l3 = newItem(l2, l3, i18n("Kernel module"), dri_info.module); | | |||
640 | } | | |||
641 | | ||||
642 | #ifdef KCM_ENABLE_OPENGLES | 657 | #ifdef KCM_ENABLE_OPENGLES | ||
643 | l3 = newItem(l2, l3, i18n("OpenGL ES extensions")); | 658 | l3 = newItem(l2, l3, i18n("OpenGL ES extensions")); | ||
644 | #else | 659 | #else | ||
645 | l3 = newItem(l2, l3, i18n("OpenGL extensions")); | 660 | l3 = newItem(l2, l3, i18n("OpenGL extensions")); | ||
646 | #endif | 661 | #endif | ||
647 | print_extension_list(gli.glExtensions, l3); | 662 | print_extension_list(gli.glExtensions, l3); | ||
648 | 663 | | |||
649 | #if KCM_HAVE_GLX | 664 | #if KCM_HAVE_GLX | ||
▲ Show 20 Lines • Show All 387 Lines • Show Last 20 Lines |
const