Currently, the enableTrueColors function in terminal does the following to check for Windows 10:
ver.dwOSVersionInfoSize = sizeof(ver).DWORD
let res = getVersionExW(addr ver)
if res == 0:
term.trueColorIsSupported = false
else:
term.trueColorIsSupported = ver.dwMajorVersion > 10 or
(ver.dwMajorVersion == 10 and (ver.dwMinorVersion > 0 or
(ver.dwMinorVersion == 0 and ver.dwBuildNumber >= 10586)))
This is wrong.
As of Windows 8, the GetVersionEx functions have been deprecated and this function will return the wrong version information as it pulls from the manifest which isn't updated (read: if upgrading from Windows 8 to 10, it returns a major version of 6 instead of 10). And lest someone think that the VerifyVersionInfo functions in the WINAPI will work, just know that they won't; they are now subject to the same manifest "bug": https://msdn.microsoft.com/en-us/library/windows/desktop/dn424972.aspx.
The best way to try and get the version of Windows is via the registry. Sadly, Window 10 introduced new keys that hold the version, so getting it would look something like this:
proc regOpenKey(root: HANDLE, subKey: WideCString, opts: DWORD, access: DWORD, key: ptr HANDLE): LONG
{.stdcall, dynlib: "advapi32", importc: "RegOpenKeyExW".}
proc regCloseKey(key: HANDLE): LONG
{.stdcall, dynlib: "advapi32", importc: "RegCloseKey".}
proc regQueryValueEx(key: HANDLE, subKey: WideCString, res: pointer, kind: pointer, data: pointer, dataSize: ptr DWORD): LONG
{.stdcall, dynlib: "advapi32", importc: "RegQueryValueExW".}
let HKEY_LOCAL_MACHINE = 0x80000002.HANDLE
let KEY_READ = 0x20019.DWORD
let KEY_WOW64_64KEY = 0x0100.DWORD
let VERSION_KEY = newWideCString(r"SOFTWARE\Microsoft\Windows NT\CurrentVersion".cstring)
proc getWindowsVersion(): tuple[major, minor: Option[int]] =
var major, minor = none[int]()
var key: HANDLE
if regOpenKey(HKEY_LOCAL_MACHINE, VERSION_KEY, 0.DWORD, KEY_READ or KEY_WOW64_64KEY, addr(key)) == 0:
let majorKey = newWideCString("CurrentMajorVersionNumber")
let minorKey = newWideCString("CurrentMinorVersionNumber")
# output from query values
var value: DWORD = 0
var size: DWORD = sizeof(value).DWORD
# get version key values
if regQueryValueEx(key, majorKey, nil.pointer, nil.pointer, addr(value), addr(size)) == 0:
major = some(value.int)
if regQueryValueEx(key, minorKey, nil.pointer, nil.pointer, addr(value), addr(size)) == 0:
minor = some(value.int)
discard regCloseKey(key)
(major, minor)
But, again, this only works on Windows 10. On versions < 10, you need to get the "CurrentVersion" key (a string), and parse it:
# ... similar to above, opening the key, but the sub key being read is different
var value = newStringOfCap(100)
var size = 100.DWORD
# get version key values
if regQueryValueEx(key, versionKey, nil.pointer, nil.pointer, value.cstring, addr(size)) == 0:
echo value
let ver = split(value.strip(), '.')
major = some(ver[0].parseInt())
minor = some(ver[1].parseInt())
I haven't gotten this half to work (it segfaults attempting to read from nil). I assume I'm loading the value wrong. My time with Nim so far has only been a couple days, so hopefully it's a quick/easy fix for someone here.
Anyway, I don't know if there are other places in the standard library that are testing against the Windows version (specifically looking for Windows 10 or higher), but the check in terminal is wrong and should be changed. And there it might be beneficial - in general - to just have an os.getVersion(): tuple[major, minor: int] method available that's cross-platform and can be used for these sorts of things.
As the above relates specifically to the terminal module, there shouldn't really be a need to test for Windows 10. It should be possible to just set the ENABLE_VIRTUAL_TERMINAL_PROCESSING mode flag on STDOUT and it either works or not. If not, it's not supported. If so, then great. Making this change would be no different (to the end user) than the current behavior. Right now it silently just doesn't even try to set it and the end result is the same: it's as if the console didn't support it.