diff options
author | Joseph W. Joshua <joejoshw@gmail.com> | 2014-06-14 22:27:57 +0300 |
---|---|---|
committer | Dirk Hohndel <dirk@hohndel.org> | 2014-06-14 12:50:35 -0700 |
commit | 1ec5e6ffc4f9f32a6d7149e52b161aeaba6adfa4 (patch) | |
tree | 58a6be26495b540d3471682a1a91369bfbd9a14e /subsurfacesysinfo.cpp | |
parent | cdd3b3d9cd5439cd56fb752e56a72045bed55f90 (diff) | |
download | subsurface-1ec5e6ffc4f9f32a6d7149e52b161aeaba6adfa4.tar.gz |
OS Detection
Implements automatic OS and architecture detection. This code has
been taken from the Qt repositories for Qt 5.4 (LGPL) and slightly
modified into the SubsurfaceSysInfo class.
Signed-off-by: Joseph W. Joshua <joejoshw@gmail.com>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Diffstat (limited to 'subsurfacesysinfo.cpp')
-rw-r--r-- | subsurfacesysinfo.cpp | 603 |
1 files changed, 603 insertions, 0 deletions
diff --git a/subsurfacesysinfo.cpp b/subsurfacesysinfo.cpp new file mode 100644 index 000000000..5d556c6fc --- /dev/null +++ b/subsurfacesysinfo.cpp @@ -0,0 +1,603 @@ +#include "subsurfacesysinfo.h" +#include <QString> +#include "qplatformdefs.h" + +#ifdef Q_OS_UNIX +#include <sys/utsname.h> +#endif + +// main part: processor type +#if defined(Q_PROCESSOR_ALPHA) +# define ARCH_PROCESSOR "alpha" +#elif defined(Q_PROCESSOR_ARM) +# define ARCH_PROCESSOR "arm" +#elif defined(Q_PROCESSOR_AVR32) +# define ARCH_PROCESSOR "avr32" +#elif defined(Q_PROCESSOR_BLACKFIN) +# define ARCH_PROCESSOR "bfin" +#elif defined(Q_PROCESSOR_X86_32) +# define ARCH_PROCESSOR "i386" +#elif defined(Q_PROCESSOR_X86_64) +# define ARCH_PROCESSOR "x86_64" +#elif defined(Q_PROCESSOR_IA64) +# define ARCH_PROCESSOR "ia64" +#elif defined(Q_PROCESSOR_MIPS) +# define ARCH_PROCESSOR "mips" +#elif defined(Q_PROCESSOR_POWER) +# define ARCH_PROCESSOR "power" +#elif defined(Q_PROCESSOR_S390) +# define ARCH_PROCESSOR "s390" +#elif defined(Q_PROCESSOR_SH) +# define ARCH_PROCESSOR "sh" +#elif defined(Q_PROCESSOR_SPARC) +# define ARCH_PROCESSOR "sparc" +#else +# define ARCH_PROCESSOR "unknown" +#endif + +// endinanness +#if defined(Q_LITTLE_ENDIAN) +# define ARCH_ENDIANNESS "little_endian" +#elif defined(Q_BIG_ENDIAN) +# define ARCH_ENDIANNESS "big_endian" +#endif + +// pointer type +#if defined(Q_OS_WIN64) || (defined(Q_OS_WINRT) && defined(_M_X64)) +# define ARCH_POINTER "llp64" +#elif defined(__LP64__) || QT_POINTER_SIZE - 0 == 8 +# define ARCH_POINTER "lp64" +#else +# define ARCH_POINTER "ilp32" +#endif + +// secondary: ABI string (includes the dash) +#if defined(__ARM_EABI__) || defined(__mips_eabi) +# define ARCH_ABI1 "-eabi" +#elif defined(_MIPS_SIM) +# if _MIPS_SIM == _ABIO32 +# define ARCH_ABI1 "-o32" +# elif _MIPS_SIM == _ABIN32 +# define ARCH_ABI1 "-n32" +# elif _MIPS_SIM == _ABI64 +# define ARCH_ABI1 "-n64" +# elif _MIPS_SIM == _ABIO64 +# define ARCH_ABI1 "-o64" +# endif +#else +# define ARCH_ABI1 "" +#endif +#if defined(__ARM_PCS_VFP) || defined(__mips_hard_float) +# define ARCH_ABI2 "-hardfloat" +#else +# define ARCH_ABI2 "" +#endif + +#define ARCH_ABI ARCH_ABI1 ARCH_ABI2 + +#define ARCH_FULL ARCH_PROCESSOR "-" ARCH_ENDIANNESS "-" ARCH_POINTER ARCH_ABI + + +#if defined(Q_OS_OSX) + +Q_CORE_EXPORT OSErr qt_mac_create_fsref(const QString &file, FSRef *fsref) +{ + return FSPathMakeRef(reinterpret_cast<const UInt8 *>(file.toUtf8().constData()), fsref, 0); +} + +Q_CORE_EXPORT void qt_mac_to_pascal_string(QString s, Str255 str, TextEncoding encoding=0, int len=-1) +{ + Q_UNUSED(encoding); + Q_UNUSED(len); + CFStringGetPascalString(QCFString(s), str, 256, CFStringGetSystemEncoding()); +} + +Q_CORE_EXPORT QString qt_mac_from_pascal_string(const Str255 pstr) { + return QCFString(CFStringCreateWithPascalString(0, pstr, CFStringGetSystemEncoding())); +} + +SubsurfaceSysInfo::MacVersion SubsurfaceSysInfo::macVersion() +{ +#if defined(Q_OS_OSX) + SInt32 gestalt_version; + if (Gestalt(gestaltSystemVersionMinor, &gestalt_version) == noErr) { + // add 2 because OS X 10.0 is 0x02 in the enum + return SubsurfaceSysInfo::MacVersion(gestalt_version + 2); + } +#elif defined(Q_OS_IOS) + return qt_ios_version(); // qtcore_mac_objc.mm +#endif + return SubsurfaceSysInfo::MV_Unknown; +} +const SubsurfaceSysInfo::MacVersion SubsurfaceSysInfo::MacintoshVersion = SubsurfaceSysInfo::macVersion(); +#elif defined(Q_OS_WIN) || defined(Q_OS_CYGWIN) || defined(Q_OS_WINCE) || defined(Q_OS_WINRT) + +QT_BEGIN_INCLUDE_NAMESPACE +#include "qt_windows.h" +QT_END_INCLUDE_NAMESPACE + +#ifndef Q_OS_WINRT +static inline OSVERSIONINFO winOsVersion() +{ + OSVERSIONINFO result = { sizeof(OSVERSIONINFO), 0, 0, 0, 0, {'\0'}}; + // GetVersionEx() has been deprecated in Windows 8.1 and will return + // only Windows 8 from that version on. +# if defined(_MSC_VER) && _MSC_VER >= 1800 +# pragma warning( push ) +# pragma warning( disable : 4996 ) +# endif + GetVersionEx(&result); +# if defined(_MSC_VER) && _MSC_VER >= 1800 +# pragma warning( pop ) +# endif +# ifndef Q_OS_WINCE + if (result.dwMajorVersion == 6 && result.dwMinorVersion == 2) { + // This could be Windows 8.1 or higher. Note that as of Windows 9, + // the major version needs to be checked as well. + DWORDLONG conditionMask = 0; + VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(conditionMask, VER_PLATFORMID, VER_EQUAL); + OSVERSIONINFOEX checkVersion = { sizeof(OSVERSIONINFOEX), result.dwMajorVersion, result.dwMinorVersion, + result.dwBuildNumber, result.dwPlatformId, {'\0'}, 0, 0, 0, 0, 0 }; + for ( ; VerifyVersionInfo(&checkVersion, VER_MAJORVERSION | VER_MINORVERSION | VER_PLATFORMID, conditionMask); ++checkVersion.dwMinorVersion) + result.dwMinorVersion = checkVersion.dwMinorVersion; + } +# endif // !Q_OS_WINCE + return result; +} +#endif // !Q_OS_WINRT + +SubsurfaceSysInfo::WinVersion SubsurfaceSysInfo::windowsVersion() +{ +#ifndef VER_PLATFORM_WIN32s +#define VER_PLATFORM_WIN32s 0 +#endif +#ifndef VER_PLATFORM_WIN32_WINDOWS +#define VER_PLATFORM_WIN32_WINDOWS 1 +#endif +#ifndef VER_PLATFORM_WIN32_NT +#define VER_PLATFORM_WIN32_NT 2 +#endif +#ifndef VER_PLATFORM_WIN32_CE +#define VER_PLATFORM_WIN32_CE 3 +#endif + + static SubsurfaceSysInfo::WinVersion winver; + if (winver) + return winver; +#ifdef Q_OS_WINRT + winver = SubsurfaceSysInfo::WV_WINDOWS8; +#else + winver = SubsurfaceSysInfo::WV_NT; + const OSVERSIONINFO osver = winOsVersion(); +#ifdef Q_OS_WINCE + DWORD qt_cever = 0; + qt_cever = osver.dwMajorVersion * 100; + qt_cever += osver.dwMinorVersion * 10; +#endif + switch (osver.dwPlatformId) { + case VER_PLATFORM_WIN32s: + winver = SubsurfaceSysInfo::WV_32s; + break; + case VER_PLATFORM_WIN32_WINDOWS: + // We treat Windows Me (minor 90) the same as Windows 98 + if (osver.dwMinorVersion == 90) + winver = SubsurfaceSysInfo::WV_Me; + else if (osver.dwMinorVersion == 10) + winver = SubsurfaceSysInfo::WV_98; + else + winver = SubsurfaceSysInfo::WV_95; + break; +#ifdef Q_OS_WINCE + case VER_PLATFORM_WIN32_CE: + if (qt_cever >= 600) + winver = SubsurfaceSysInfo::WV_CE_6; + if (qt_cever >= 500) + winver = SubsurfaceSysInfo::WV_CE_5; + else if (qt_cever >= 400) + winver = SubsurfaceSysInfo::WV_CENET; + else + winver = SubsurfaceSysInfo::WV_CE; + break; +#endif + default: // VER_PLATFORM_WIN32_NT + if (osver.dwMajorVersion < 5) { + winver = SubsurfaceSysInfo::WV_NT; + } else if (osver.dwMajorVersion == 5 && osver.dwMinorVersion == 0) { + winver = SubsurfaceSysInfo::WV_2000; + } else if (osver.dwMajorVersion == 5 && osver.dwMinorVersion == 1) { + winver = SubsurfaceSysInfo::WV_XP; + } else if (osver.dwMajorVersion == 5 && osver.dwMinorVersion == 2) { + winver = SubsurfaceSysInfo::WV_2003; + } else if (osver.dwMajorVersion == 6 && osver.dwMinorVersion == 0) { + winver = SubsurfaceSysInfo::WV_VISTA; + } else if (osver.dwMajorVersion == 6 && osver.dwMinorVersion == 1) { + winver = SubsurfaceSysInfo::WV_WINDOWS7; + } else if (osver.dwMajorVersion == 6 && osver.dwMinorVersion == 2) { + winver = SubsurfaceSysInfo::WV_WINDOWS8; + } else if (osver.dwMajorVersion == 6 && osver.dwMinorVersion == 3) { + winver = SubsurfaceSysInfo::WV_WINDOWS8_1; + } else { + qWarning("Qt: Untested Windows version %d.%d detected!", + int(osver.dwMajorVersion), int(osver.dwMinorVersion)); + winver = SubsurfaceSysInfo::WV_NT_based; + } + } + +#ifdef QT_DEBUG + { + QByteArray override = qgetenv("QT_WINVER_OVERRIDE"); + if (override.isEmpty()) + return winver; + + if (override == "Me") + winver = SubsurfaceSysInfo::WV_Me; + if (override == "95") + winver = SubsurfaceSysInfo::WV_95; + else if (override == "98") + winver = SubsurfaceSysInfo::WV_98; + else if (override == "NT") + winver = SubsurfaceSysInfo::WV_NT; + else if (override == "2000") + winver = SubsurfaceSysInfo::WV_2000; + else if (override == "2003") + winver = SubsurfaceSysInfo::WV_2003; + else if (override == "XP") + winver = SubsurfaceSysInfo::WV_XP; + else if (override == "VISTA") + winver = SubsurfaceSysInfo::WV_VISTA; + else if (override == "WINDOWS7") + winver = SubsurfaceSysInfo::WV_WINDOWS7; + else if (override == "WINDOWS8") + winver = SubsurfaceSysInfo::WV_WINDOWS8; + else if (override == "WINDOWS8_1") + winver = SubsurfaceSysInfo::WV_WINDOWS8_1; + } +#endif +#endif // !Q_OS_WINRT + + return winver; +} + +static const char *winVer_helper() +{ + switch (int(SubsurfaceSysInfo::WindowsVersion)) { + case SubsurfaceSysInfo::WV_NT: + return "NT"; + case SubsurfaceSysInfo::WV_2000: + return "2000"; + case SubsurfaceSysInfo::WV_XP: + return "XP"; + case SubsurfaceSysInfo::WV_2003: + return "2003"; + case SubsurfaceSysInfo::WV_VISTA: + return "Vista"; + case SubsurfaceSysInfo::WV_WINDOWS7: + return "7"; + case SubsurfaceSysInfo::WV_WINDOWS8: + return "8"; + case SubsurfaceSysInfo::WV_WINDOWS8_1: + return "8.1"; + + case SubsurfaceSysInfo::WV_CE: + return "CE"; + case SubsurfaceSysInfo::WV_CENET: + return "CENET"; + case SubsurfaceSysInfo::WV_CE_5: + return "CE5"; + case SubsurfaceSysInfo::WV_CE_6: + return "CE6"; + } + // unknown, future version + return 0; +} + +const SubsurfaceSysInfo::WinVersion SubsurfaceSysInfo::WindowsVersion = SubsurfaceSysInfo::windowsVersion(); + +#endif + +#if defined(Q_OS_UNIX) +# if (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || defined(Q_OS_FREEBSD) +# define USE_ETC_OS_RELEASE +# endif +struct QUnixOSVersion +{ + // from uname(2) + QString sysName; + QString sysNameLower; + QString sysRelease; + +# if !defined(Q_OS_ANDROID) && !defined(Q_OS_BLACKBERRY) && !defined(Q_OS_MAC) + // from /etc/os-release or guessed + QString versionIdentifier; // ${ID}_$VERSION_ID + QString versionText; // $PRETTY_NAME +# endif +}; + + +#ifdef USE_ETC_OS_RELEASE +static QString unquote(const char *begin, const char *end) +{ + if (*begin == '"') { + Q_ASSERT(end[-1] == '"'); + return QString::fromLatin1(begin + 1, end - begin - 2); + } + return QString::fromLatin1(begin, end - begin); +} + + +static bool readEtcOsRelease(QUnixOSVersion &v) +{ + // we're avoiding QFile here + + int fd = QT_OPEN("/etc/os-release", O_RDONLY); + if (fd == -1) + return false; + + QT_STATBUF sbuf; + if (QT_FSTAT(fd, &sbuf) == -1) { + QT_CLOSE(fd); + return false; + } + + QString partialIdentifier; + QByteArray buffer(sbuf.st_size, Qt::Uninitialized); + buffer.resize(QT_READ(fd, buffer.data(), sbuf.st_size)); + QT_CLOSE(fd); + + const char *ptr = buffer.constData(); + const char *end = buffer.constEnd(); + const char *eol; + for ( ; ptr != end; ptr = eol + 1) { + static const char idString[] = "ID="; + static const char prettyNameString[] = "PRETTY_NAME="; + static const char versionIdString[] = "VERSION_ID="; + + // find the end of the line after ptr + eol = static_cast<const char *>(memchr(ptr, '\n', end - ptr)); + if (!eol) + eol = end - 1; + + int cmp = strncmp(ptr, idString, strlen(idString)); + if (cmp < 0) + continue; + if (cmp == 0) { + ptr += strlen(idString); + QString id = unquote(ptr, eol); + if (partialIdentifier.isNull()) + partialIdentifier = id; + else + v.versionIdentifier = id + QLatin1Char('_') + partialIdentifier; + continue; + } + + cmp = strncmp(ptr, prettyNameString, strlen(prettyNameString)); + if (cmp < 0) + continue; + if (cmp == 0) { + ptr += strlen(prettyNameString); + v.versionText = unquote(ptr, eol); + continue; + } + + cmp = strncmp(ptr, versionIdString, strlen(versionIdString)); + if (cmp < 0) + continue; + if (cmp == 0) { + ptr += strlen(versionIdString); + QString id = unquote(ptr, eol); + if (partialIdentifier.isNull()) + partialIdentifier = id; + else + v.versionIdentifier = partialIdentifier + QLatin1Char('_') + id; + continue; + } + } + + return true; +} +#endif // USE_ETC_OS_RELEASE + +static QUnixOSVersion detectUnixVersion() +{ + QUnixOSVersion v; + struct utsname u; + if (uname(&u) != -1) { + v.sysName = QString::fromLatin1(u.sysname); + v.sysNameLower = v.sysName.toLower(); + v.sysRelease = QString::fromLatin1(u.release); + } else { + v.sysName = QLatin1String("Detection failed"); + // leave sysNameLower & sysRelease unset + } + +# if !defined(Q_OS_ANDROID) && !defined(Q_OS_BLACKBERRY) && !defined(Q_OS_MAC) +# ifdef USE_ETC_OS_RELEASE + if (readEtcOsRelease(v)) + return v; +# endif + + if (!v.sysNameLower.isEmpty()) { + // will produce "qnx_6.5" or "sunos_5.9" + v.versionIdentifier = v.sysNameLower + QLatin1Char('_') + v.sysRelease; + } +# endif + + return v; +} +#endif + +QString SubsurfaceSysInfo::unknownText() +{ + return QStringLiteral("unknown"); +} + +QString SubsurfaceSysInfo::cpuArchitecture() +{ + return QStringLiteral(ARCH_PROCESSOR); +} + +QString SubsurfaceSysInfo::fullCpuArchitecture() +{ + return QLatin1String(ARCH_FULL); +} + +QString SubsurfaceSysInfo::osType() +{ +#if defined(Q_OS_WINPHONE) + return QStringLiteral("winphone"); +#elif defined(Q_OS_WINRT) + return QStringLiteral("winrt"); +#elif defined(Q_OS_WINCE) + return QStringLiteral("wince"); +#elif defined(Q_OS_WIN) + return QStringLiteral("windows"); + +#elif defined(Q_OS_BLACKBERRY) + return QStringLiteral("blackberry"); +#elif defined(Q_OS_QNX) + return QStringLiteral("qnx"); + +#elif defined(Q_OS_ANDROID) + return QStringLiteral("android"); +#elif defined(Q_OS_LINUX) + return QStringLiteral("linux"); + +#elif defined(Q_OS_IOS) + return QStringLiteral("ios"); +#elif defined(Q_OS_OSX) + return QStringLiteral("osx"); +#elif defined(Q_OS_DARWIN) + return QStringLiteral("darwin"); + +#elif defined(Q_OS_FREEBSD_KERNEL) + return QStringLiteral("freebsd"); +#elif defined(Q_OS_UNIX) + QUnixOSVersion unixOsVersion = detectUnixVersion(); + if (!unixOsVersion.sysNameLower.isEmpty()) + return unixOsVersion.sysNameLower; +#endif + return unknownText(); +} + +QString SubsurfaceSysInfo::osKernelVersion() +{ +#ifdef Q_OS_WINRT + // TBD + return QString(); +#elif defined(Q_OS_WIN) + const OSVERSIONINFO osver = winOsVersion(); + return QString::number(int(osver.dwMajorVersion)) + QLatin1Char('.') + QString::number(int(osver.dwMinorVersion)) + + QLatin1Char('.') + QString::number(int(osver.dwBuildNumber)); +#else + return detectUnixVersion().sysRelease; +#endif +} + +QString SubsurfaceSysInfo::osVersion() +{ +#if defined(Q_OS_IOS) + int major = (int(MacintoshVersion) >> 4) & 0xf; + int minor = int(MacintoshVersion) & 0xf; + if (Q_LIKELY(major < 10 && minor < 10)) { + char buf[4] = { char(major + '0'), '.', char(minor + '0'), '\0' }; + return QString::fromLatin1(buf, 3); + } + return QString::number(major) + QLatin1Char('.') + QString::number(minor); +#elif defined(Q_OS_OSX) + int minor = int(MacintoshVersion) - 2; // we're not running on Mac OS 9 + Q_ASSERT(minor < 100); + char buf[] = "10.0\0"; + if (Q_LIKELY(minor < 10)) { + buf[3] += minor; + } else { + buf[3] += minor / 10; + buf[4] = '0' + minor % 10; + } + return QString::fromLatin1(buf); +#elif defined(Q_OS_WIN) + const char *version = winVer_helper(); + if (version) + return QString::fromLatin1(version).toLower(); + // fall through + + // Android and Blackberry should not fall through to the Unix code +#elif defined(Q_OS_ANDROID) + // TBD +#elif defined(Q_OS_BLACKBERRY) + deviceinfo_details_t *deviceInfo; + if (deviceinfo_get_details(&deviceInfo) == BPS_SUCCESS) { + QString bbVersion = QString::fromLatin1(deviceinfo_details_get_device_os_version(deviceInfo)); + deviceinfo_free_details(&deviceInfo); + return bbVersion; + } +#elif defined(Q_OS_UNIX) + QUnixOSVersion unixOsVersion = detectUnixVersion(); + if (!unixOsVersion.versionIdentifier.isEmpty()) + return unixOsVersion.versionIdentifier; +#endif + + // fallback + return unknownText(); +} + +QString SubsurfaceSysInfo::prettyOsName() +{ +#if defined(Q_OS_IOS) + return QLatin1String("iOS ") + osVersion(); +#elif defined(Q_OS_OSX) + // get the known codenames + const char *basename = 0; + switch (int(MacintoshVersion)) { + case MV_CHEETAH: + case MV_PUMA: + case MV_JAGUAR: + case MV_PANTHER: + case MV_TIGER: + // This version of Qt does not run on those versions of OS X + // so this case label will never be reached + Q_UNREACHABLE(); + break; + case MV_LEOPARD: + basename = "Mac OS X Leopard ("; + break; + case MV_SNOWLEOPARD: + basename = "Mac OS X Snow Leopard ("; + break; + case MV_LION: + basename = "Mac OS X Lion ("; + break; + case MV_MOUNTAINLION: + basename = "OS X Mountain Lion ("; + break; + case MV_MAVERICKS: + basename = "OS X Mavericks ("; + break; + case MV_YOSEMITE: + basename = "OS X Yosemite ("; + break; + } + if (basename) + return QLatin1String(basename) + osVersion() + QLatin1Char(')'); + + // a future version of OS X + return QLatin1String("OS X ") + osVersion(); +#elif defined(Q_OS_WINPHONE) + return QLatin1String("Windows Phone ") + QLatin1String(winVer_helper()); +#elif defined(Q_OS_WIN) + return QLatin1String("Windows ") + QLatin1String(winVer_helper()); +#elif defined(Q_OS_ANDROID) + return QLatin1String("Android ") + osVersion(); +#elif defined(Q_OS_BLACKBERRY) + return QLatin1String("BlackBerry ") + osVersion(); +#elif defined(Q_OS_UNIX) + QUnixOSVersion unixOsVersion = detectUnixVersion(); + if (unixOsVersion.versionText.isEmpty()) + return unixOsVersion.sysName; + else + return unixOsVersion.sysName + QLatin1String(" (") + unixOsVersion.versionText + QLatin1Char(')'); +#else + return unknownText(); +#endif +} |