Linux multi-monitor fullscreen support
[ryzomcore.git] / nel / src / misc / system_utils.cpp
blob8c01b227c6b6b9717975b2ef03c329bd62291ff9
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2014-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "stdmisc.h"
21 #include "nel/misc/system_utils.h"
22 #include "nel/misc/utf_string_view.h"
24 #ifdef NL_OS_WINDOWS
25 #define INITGUID
26 #include <ddraw.h>
27 #include <windows.h>
28 #include <string.h>
29 #include <stdio.h>
30 #ifdef DXGI_STATUS_OCCLUDED
31 #undef DXGI_STATUS_OCCLUDED
32 #undef DXGI_STATUS_CLIPPED
33 #undef DXGI_STATUS_NO_REDIRECTION
34 #undef DXGI_STATUS_NO_DESKTOP_ACCESS
35 #undef DXGI_STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE
36 #undef DXGI_STATUS_MODE_CHANGED
37 #undef DXGI_STATUS_MODE_CHANGE_IN_PROGRESS
38 #endif
39 #ifdef DXGI_ERROR_INVALID_CALL
40 #undef DXGI_ERROR_INVALID_CALL
41 #undef DXGI_ERROR_NOT_FOUND
42 #undef DXGI_ERROR_MORE_DATA
43 #undef DXGI_ERROR_UNSUPPORTED
44 #undef DXGI_ERROR_DEVICE_REMOVED
45 #undef DXGI_ERROR_DEVICE_HUNG
46 #undef DXGI_ERROR_DEVICE_RESET
47 #undef DXGI_ERROR_WAS_STILL_DRAWING
48 #undef DXGI_ERROR_FRAME_STATISTICS_DISJOINT
49 #undef DXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE
50 #undef DXGI_ERROR_DRIVER_INTERNAL_ERROR
51 #undef DXGI_ERROR_NONEXCLUSIVE
52 #undef DXGI_ERROR_NOT_CURRENTLY_AVAILABLE
53 #undef DXGI_ERROR_REMOTE_CLIENT_DISCONNECTED
54 #undef DXGI_ERROR_REMOTE_OUTOFMEMORY
55 #endif
56 #include <dxgi.h>
57 #include <initguid.h>
58 #include <CGuid.h>
59 # include <ObjBase.h>
60 # ifdef _WIN32_WINNT_WIN7
61 // only supported by Windows 7 Platform SDK
62 # include <ShObjIdl.h>
63 # define TASKBAR_PROGRESS 1
64 # endif
65 #elif defined(NL_OS_UNIX) && !defined(NL_OS_MAC)
66 #include "nel/misc/file.h"
67 #endif
69 #ifdef DEBUG_NEW
70 #define new DEBUG_NEW
71 #endif
73 using namespace std;
75 // Key in registry
76 static string RootKey;
77 static const uint32 KeyMaxLength = 1024;
79 namespace NLMISC {
81 nlWindow CSystemUtils::s_window = EmptyWindow;
83 #ifdef NL_OS_WINDOWS
84 static bool s_mustUninit = false;
85 #endif
87 bool CSystemUtils::init()
89 #ifdef NL_OS_WINDOWS
90 // initialize COM
91 if (!s_mustUninit)
93 HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
94 if (FAILED(hr)) return false;
96 s_mustUninit = true;
98 #endif
100 return true;
103 bool CSystemUtils::uninit()
105 #ifdef NL_OS_WINDOWS
106 // uninitialize COM
107 if (s_mustUninit)
109 CoUninitialize();
111 s_mustUninit = false;
113 #endif
115 return true;
118 void CSystemUtils::setWindow(nlWindow window)
120 s_window = window;
123 bool CSystemUtils::updateProgressBar(uint value, uint total)
125 #ifdef TASKBAR_PROGRESS
126 if (s_window == NULL)
128 nldebug("No window has be set with CSystemUtils::setWindow(), progress bar can't be displayed");
129 return false;
132 ITaskbarList3 *pTaskbarList = NULL;
134 // instanciate the taskbar control COM object
135 HRESULT hr = CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pTaskbarList));
136 // error can be ignored because Windows versions before Windows 7 doesn't support it
137 if (FAILED(hr) || !pTaskbarList) return false;
139 if (total)
141 // update the taskbar progress
142 hr = pTaskbarList->SetProgressValue(s_window, (ULONGLONG)value, (ULONGLONG)total);
144 else
146 // don't update anymore the progress
147 hr = pTaskbarList->SetProgressState(s_window, value == 0 ? TBPF_INDETERMINATE:TBPF_NOPROGRESS);
150 // release the interface
151 pTaskbarList->Release();
153 #endif // TASKBAR_PROGRESS
155 return true;
158 bool CSystemUtils::copyTextToClipboard(const std::string &text)
160 if (text.empty()) return false;
162 bool res = false;
164 #ifdef NL_OS_WINDOWS
165 if (OpenClipboard(NULL))
167 // check if unicode format is supported by clipboard
168 bool isUnicode = (IsClipboardFormatAvailable(CF_UNICODETEXT) == TRUE);
170 // allocates a buffer to copy text in global memory
171 std::string textMbcs;
172 std::wstring textWide;
173 if (!isUnicode)
175 textMbcs = NLMISC::utf8ToMbcs(text); // Prefer system for API
176 if (text.size() && !textMbcs.size())
177 textMbcs = CUtfStringView(text).toAscii(); // Fallback to 7-bit ASCII
179 else
181 textWide = NLMISC::utf8ToWide(text); // Prefer system for API
182 if (text.size() && !textWide.size())
183 textWide = CUtfStringView(text).toWide();
185 HGLOBAL mem = GlobalAlloc(GHND | GMEM_DDESHARE, isUnicode
186 ? ((textWide.size() + 1) * sizeof(wchar_t))
187 : (textMbcs.size() + 1));
189 if (mem)
191 // create a lock on this buffer
192 void *hLock = GlobalLock(mem);
193 if (hLock)
195 // copy text to this buffer
196 if (isUnicode)
197 wcscpy((wchar_t *)hLock, textWide.c_str());
198 else
199 strcpy((char *)hLock, textMbcs.c_str());
201 // unlock buffer
202 GlobalUnlock(mem);
204 // empty clipboard
205 EmptyClipboard();
207 // set new data to clipboard in the right format
208 SetClipboardData(isUnicode ? CF_UNICODETEXT : CF_TEXT, mem);
210 res = true;
214 CloseClipboard();
216 #endif
218 return res;
221 bool CSystemUtils::pasteTextFromClipboard(std::string &text)
223 bool res = false;
225 #ifdef NL_OS_WINDOWS
226 if (OpenClipboard(NULL))
228 // check if unicode format is supported by clipboard
229 bool isUnicode = (IsClipboardFormatAvailable(CF_UNICODETEXT) == TRUE);
231 // get data from clipboard (if not of this type, they are converted)
232 // warning, this code can't be debuggued in VC++ IDE, hObj will be always NULL
233 HANDLE hObj = GetClipboardData(isUnicode ? CF_UNICODETEXT : CF_TEXT);
235 if (hObj)
237 // create a lock on clipboard data
238 void *hLock = GlobalLock(hObj);
240 if (hLock != NULL)
242 // retrieve clipboard data
243 if (isUnicode)
245 const wchar_t *str = (const wchar_t *)hLock;
246 text = NLMISC::wideToUtf8(str); // Prefer system for API
247 if (!text.size() && str[0])
248 text = CUtfStringView(str).toUtf8();
249 else
250 text = CUtfStringView(text).toUtf8(true); // Sanitize UTF-8 user input
252 else
254 const char *str = (const char *)hLock;
255 text = NLMISC::mbcsToUtf8(str); // Prefer system for API
256 if (!text.size() && str[0])
257 text = CUtfStringView(str).toAscii(); // Fallback to 7-bit ASCII
258 else
259 text = CUtfStringView(text).toUtf8(true); // Sanitize UTF-8 user input
262 // unlock data
263 GlobalUnlock(hObj);
265 res = true;
269 CloseClipboard();
271 #endif
273 return res;
276 bool CSystemUtils::supportUnicode()
278 static bool init = false;
279 static bool unicodeSupported = false;
280 if (!init)
282 init = true;
283 #ifdef NL_OS_WINDOWS
284 OSVERSIONINFOA osvi;
285 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
287 // get Windows version
288 if (GetVersionExA(&osvi))
290 if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
292 // unicode is supported since Windows NT 4.0
293 if (osvi.dwMajorVersion >= 4)
295 unicodeSupported = true;
299 #else
300 unicodeSupported = true;
301 #endif
303 return unicodeSupported;
306 bool CSystemUtils::isAzertyKeyboard()
308 #ifdef NL_OS_WINDOWS
309 uint16 klId = uint16((uintptr_t)GetKeyboardLayout(0) & 0xFFFF);
310 // 0x040c is French, 0x080c is Belgian
311 if (klId == 0x040c || klId == 0x080c)
312 return true;
313 #endif
314 return false;
317 bool CSystemUtils::isScreensaverEnabled()
319 bool res = false;
320 #ifdef NL_OS_WINDOWS
321 // old code, is not working anymore
322 // BOOL bRetValue;
323 // SystemParametersInfoA(SPI_GETSCREENSAVEACTIVE, 0, &bRetValue, 0);
324 // res = (bRetValue == TRUE);
325 HKEY hKeyScreenSaver = NULL;
326 LSTATUS lReturn = RegOpenKeyExA(HKEY_CURRENT_USER, "Control Panel\\Desktop", 0, KEY_QUERY_VALUE, &hKeyScreenSaver);
327 if (lReturn == ERROR_SUCCESS)
329 DWORD dwType = 0L;
330 DWORD dwSize = KeyMaxLength;
331 unsigned char Buffer[KeyMaxLength] = {0};
333 lReturn = RegQueryValueExA(hKeyScreenSaver, "SCRNSAVE.EXE", NULL, &dwType, NULL, &dwSize);
334 // if SCRNSAVE.EXE is present, check also if it's empty
335 if (lReturn == ERROR_SUCCESS)
336 res = (Buffer[0] != '\0');
338 RegCloseKey(hKeyScreenSaver);
339 #endif
340 return res;
343 bool CSystemUtils::enableScreensaver(bool screensaver)
345 bool res = false;
346 #ifdef NL_OS_WINDOWS
347 res = (SystemParametersInfoA(SPI_SETSCREENSAVEACTIVE, screensaver ? TRUE:FALSE, NULL, 0) == TRUE);
348 #endif
349 return res;
352 std::string CSystemUtils::getRootKey()
354 return RootKey;
357 void CSystemUtils::setRootKey(const std::string &root)
359 RootKey = root;
362 string CSystemUtils::getRegKey(const string &entry)
364 string ret;
365 #ifdef NL_OS_WINDOWS
366 HKEY hkey;
368 if (RegOpenKeyExW(HKEY_CURRENT_USER, nlUtf8ToWide(RootKey), 0, KEY_READ, &hkey) == ERROR_SUCCESS)
370 DWORD dwType = 0L;
371 DWORD dwSize = KeyMaxLength;
372 wchar_t buffer[KeyMaxLength];
374 if (RegQueryValueExW(hkey, nlUtf8ToWide(entry), NULL, &dwType, (LPBYTE)buffer, &dwSize) != ERROR_SUCCESS)
376 nlwarning("Can't get the reg key '%s'", entry.c_str());
378 else
380 ret = wideToUtf8(buffer);
383 RegCloseKey(hkey);
385 else
387 nlwarning("Can't get the reg key '%s'", entry.c_str());
389 #endif
390 return ret;
393 bool CSystemUtils::setRegKey(const string &valueName, const string &value)
395 bool res = false;
396 #ifdef NL_OS_WINDOWS
397 HKEY hkey;
398 DWORD dwDisp;
400 if (RegCreateKeyExW(HKEY_CURRENT_USER, nlUtf8ToWide(RootKey), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, &dwDisp) == ERROR_SUCCESS)
402 // we must use the real Unicode string size in bytes
403 std::wstring wvalue = nlUtf8ToWide(value);
404 if (RegSetValueExW(hkey, nlUtf8ToWide(valueName), 0, REG_SZ, (const BYTE *)wvalue.c_str(), (wvalue.size() + 1) * sizeof(WCHAR)) == ERROR_SUCCESS)
405 res = true;
406 RegCloseKey(hkey);
408 else
410 nlwarning("Can't set the reg key '%s' '%s'", valueName.c_str(), value.c_str());
412 #endif
414 return res;
417 uint CSystemUtils::getCurrentColorDepth()
419 uint depth = 0;
421 #ifdef NL_OS_WINDOWS
422 HWND desktopWnd = GetDesktopWindow();
423 if (desktopWnd)
425 HDC desktopDC = GetWindowDC(desktopWnd);
426 if (desktopDC)
428 depth = (uint) GetDeviceCaps(desktopDC, BITSPIXEL);
429 ReleaseDC(desktopWnd, desktopDC);
432 #else
433 depth = 24; // temporary fix for compilation under Linux
435 Display *display = XOpenDisplay(NULL);
436 if (display)
438 depth = (uint) DefaultDepth(display, DefaultScreen(display));
439 XCloseDisplay(display);
442 #endif
443 return depth;
446 /// Detect whether the current process is a windowed application. Return true if definitely yes, false if unknown
447 bool CSystemUtils::detectWindowedApplication()
449 #ifdef NL_OS_WINDOWS
450 if (GetConsoleWindow() == NULL)
451 return true;
452 #endif
453 return false;
456 #ifdef NL_OS_WINDOWS
457 #ifndef SAFE_RELEASE
458 #define SAFE_RELEASE(p) { if (p) { (p)->Release(); (p) = NULL; } }
459 #endif
461 typedef HRESULT (WINAPI* LPDIRECTDRAWCREATE)(GUID FAR *lpGUID, LPDIRECTDRAW FAR *lplpDD, IUnknown FAR *pUnkOuter);
462 typedef HRESULT (WINAPI* LPCREATEDXGIFACTORY)(REFIID, void**);
464 static std::string FormatError(HRESULT hr)
466 return NLMISC::toString("%s (0x%x)", formatErrorMessage(hr).c_str(), hr);
469 struct SAdapter
471 uint id;
472 std::string name;
473 sint memory;
474 GUID guid;
475 HMONITOR hMonitor;
476 bool found;
478 SAdapter()
480 id = 0;
481 memory = -1;
482 guid = GUID_NULL;
483 hMonitor = NULL;
484 found = false;
488 static std::list<SAdapter> s_dxgiAdapters;
490 static void EnumerateUsingDXGI(IDXGIFactory *pDXGIFactory)
492 nlassert(pDXGIFactory != NULL);
494 for(uint index = 0; ; ++index)
496 IDXGIAdapter *pAdapter = NULL;
497 HRESULT hr = pDXGIFactory->EnumAdapters(index, &pAdapter);
498 // DXGIERR_NOT_FOUND is expected when the end of the list is hit
499 if (FAILED(hr)) break;
501 DXGI_ADAPTER_DESC desc;
502 memset(&desc, 0, sizeof(DXGI_ADAPTER_DESC));
504 if (SUCCEEDED(pAdapter->GetDesc(&desc)))
506 SAdapter adapter;
507 adapter.id = index;
508 adapter.name = wideToUtf8(desc.Description);
509 adapter.memory = desc.DedicatedVideoMemory / 1024;
510 adapter.found = true;
512 nldebug("DXGI Adapter: %u - %s - DedicatedVideoMemory: %d KiB", index, adapter.name.c_str(), adapter.memory);
514 s_dxgiAdapters.push_back(adapter);
517 SAFE_RELEASE(pAdapter);
521 BOOL WINAPI DDEnumCallbackEx(GUID FAR* lpGUID, LPSTR lpDriverDescription, LPSTR lpDriverName, LPVOID lpContext, HMONITOR hm)
523 SAdapter * pAdapter = (SAdapter*)lpContext;
525 if (pAdapter->hMonitor == hm)
527 pAdapter->name = lpDriverDescription;
528 pAdapter->guid = *lpGUID;
529 pAdapter->found = true;
532 return TRUE;
535 #endif
537 sint CSystemUtils::getTotalVideoMemory()
539 sint res = -1;
541 #if defined(NL_OS_WINDOWS)
542 // using DXGI
543 HINSTANCE hDXGI = LoadLibraryA("dxgi.dll");
545 if (hDXGI)
548 // We prefer the use of DXGI 1.1
549 LPCREATEDXGIFACTORY pCreateDXGIFactory = (LPCREATEDXGIFACTORY)GetProcAddress(hDXGI, "CreateDXGIFactory1");
551 if (!pCreateDXGIFactory)
553 pCreateDXGIFactory = (LPCREATEDXGIFACTORY)GetProcAddress(hDXGI, "CreateDXGIFactory");
556 if (pCreateDXGIFactory)
558 IDXGIFactory *pDXGIFactory = NULL;
559 HRESULT hr = pCreateDXGIFactory(__uuidof(IDXGIFactory), (LPVOID*)&pDXGIFactory);
561 if (SUCCEEDED(hr))
563 EnumerateUsingDXGI(pDXGIFactory);
565 SAFE_RELEASE(pDXGIFactory);
567 if (!s_dxgiAdapters.empty())
569 // TODO: determine what adapter is used by NeL
570 res = s_dxgiAdapters.front().memory;
572 else
574 nlwarning("Unable to find an DXGI adapter");
577 else
579 nlwarning("Unable to create DXGI factory");
582 else
584 nlwarning("dxgi.dll missing entry-point");
587 FreeLibrary(hDXGI);
590 if (res == -1)
592 // using DirectDraw
593 HMODULE hInstDDraw = LoadLibraryA("ddraw.dll");
595 if (hInstDDraw)
597 SAdapter adapter;
598 adapter.hMonitor = MonitorFromWindow(s_window, MONITOR_DEFAULTTONULL);
600 LPDIRECTDRAWENUMERATEEXA pDirectDrawEnumerateEx = (LPDIRECTDRAWENUMERATEEXA)GetProcAddress(hInstDDraw, "DirectDrawEnumerateExA");
601 LPDIRECTDRAWCREATE pDDCreate = (LPDIRECTDRAWCREATE)GetProcAddress(hInstDDraw, "DirectDrawCreate");
603 if (pDirectDrawEnumerateEx && pDDCreate)
605 HRESULT hr = pDirectDrawEnumerateEx(DDEnumCallbackEx, (VOID*)&adapter, DDENUM_ATTACHEDSECONDARYDEVICES);
607 if (SUCCEEDED(hr) && adapter.found)
609 LPDIRECTDRAW pDDraw = NULL;
610 hr = pDDCreate(&adapter.guid, &pDDraw, NULL);
612 if (SUCCEEDED(hr))
614 LPDIRECTDRAW7 pDDraw7 = NULL;
615 hr = pDDraw->QueryInterface(IID_IDirectDraw7, (VOID**)&pDDraw7);
617 if (SUCCEEDED(hr))
619 DDSCAPS2 ddscaps;
620 memset(&ddscaps, 0, sizeof(DDSCAPS2));
621 ddscaps.dwCaps = DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM;
623 DWORD pdwAvailableVidMem;
624 hr = pDDraw7->GetAvailableVidMem(&ddscaps, &pdwAvailableVidMem, NULL);
626 if (SUCCEEDED(hr))
628 res = (sint)pdwAvailableVidMem / 1024;
629 nlinfo("DirectDraw Adapter: %s - DedicatedVideoMemory: %d KiB", adapter.name.c_str(), adapter.memory);
631 else
633 nlwarning("Unable to get DirectDraw available video memory: %s", FormatError(hr).c_str());
636 SAFE_RELEASE(pDDraw7);
638 else
640 nlwarning("Unable to query IDirectDraw7 interface: %s", FormatError(hr).c_str());
643 else
645 nlwarning("Unable to call DirectDrawCreate: %s", FormatError(hr).c_str());
648 else
650 nlwarning("Unable to enumerate DirectDraw adapters (%s): %s", (adapter.found ? "found":"not found"), FormatError(hr).c_str());
653 else
655 nlwarning("Unable to get pointer on DirectDraw functions (DirectDrawEnumerateExA %p, DirectDrawCreate %p)", pDirectDrawEnumerateEx, pDDCreate);
658 FreeLibrary(hInstDDraw);
660 else
662 nlwarning("Unable to load ddraw.dll");
665 #elif defined(NL_OS_MAC)
666 // the right method is using OpenGL
667 #else
668 if (res == -1)
670 // use nvidia-smi
671 std::string command = "nvidia-smi -q -d MEMORY";
673 std::string out = getCommandOutput(command);
675 if (out.empty())
677 nlwarning("Unable to launch %s", command.c_str());
679 else
681 std::vector<std::string> lines;
682 explode(out, std::string("\n"), lines, true);
684 // process each line
685 for(uint i = 0; i < lines.size(); ++i)
687 // Total : 62 MB
689 std::string line = lines[i];
691 // find Total line
692 std::string::size_type pos = line.find("Total");
693 if (pos == std::string::npos) continue;
694 pos += 6;
696 // find separator
697 pos = line.find(':', pos);
698 if (pos == std::string::npos) continue;
699 pos += 2;
701 // find units
702 std::string::size_type posUnits = line.find(' ', pos);
703 if (posUnits == std::string::npos) continue;
704 ++posUnits;
706 // found device ID
707 std::string memory = line.substr(pos, posUnits-pos-1);
708 std::string units = line.substr(posUnits);
710 // convert video memory to sint
711 if (NLMISC::fromString(memory, res))
713 if (units == "MB")
715 res *= 1024;
717 else if (units == "GB")
719 res *= 1024 * 1024;
721 else
723 // reset to use other methods
724 res = -1;
726 nlwarning("nvidia-smi reported %d %s as wrong video memory units", res, units.c_str());
727 break;
730 nlinfo("nvidia-smi reported %d KiB of video memory", res);
732 else
734 // reset to use other methods
735 res = -1;
738 break;
743 if (res == -1)
745 // under Linux, no method is really reliable...
746 NLMISC::CIFile file;
748 std::string logFile = "/var/log/Xorg.0.log";
750 // parse last Xorg.0.log
751 if (file.open(logFile, true))
753 char buffer[256];
755 while(!file.eof())
757 file.getline(buffer, 256);
759 if (buffer[0] == '\0') break;
761 std::string line(buffer);
763 // nvidia driver
764 std::string::size_type pos = line.find(") NVIDIA(");
766 if (pos != std::string::npos)
768 // [ 20.883] (--) NVIDIA(0): Memory: 2097152 kBytes
769 // [ 28.515] (--) NVIDIA(0): Memory: 262144 kBytes
770 pos = line.find("Memory: ", pos);
772 // found memory line
773 if (pos == std::string::npos) continue;
774 pos += 8;
776 std::string::size_type posUnits = line.find(" kBytes", pos);
778 // found units in KiB
779 if (posUnits == std::string::npos) continue;
781 std::string videoMemory = line.substr(pos, posUnits-pos);
783 if (!NLMISC::fromString(videoMemory, res)) continue;
785 nlinfo("Xorg NVIDIA driver reported %d KiB of video memory", res);
786 break;
789 // intel driver
790 pos = line.find(") intel(");
792 if (pos != std::string::npos)
794 // (**) intel(0): VideoRam: 131072 KB
795 pos = line.find("VideoRam: ", pos);
797 // found memory line
798 if (pos == std::string::npos) continue;
799 pos += 10;
801 std::string::size_type posUnits = line.find(" KB", pos);
803 // found units in KiB
804 if (posUnits == std::string::npos) continue;
806 std::string videoMemory = line.substr(pos, posUnits-pos);
808 if (!NLMISC::fromString(videoMemory, res)) continue;
810 nlinfo("Xorg Intel driver reported %d KiB of video memory", res);
811 break;
814 // TODO: other drivers: fglrx (ATI), radeon (ATI)
817 file.close();
821 if (res == -1)
823 // use lspci
824 std::string command = "lspci";
826 std::string out = getCommandOutput(command);
828 if (out.empty())
830 nlwarning("Unable to launch %s", command.c_str());
832 else
834 std::vector<std::string> lines;
835 std::string deviceId;
837 explode(out, std::string("\n"), lines, true);
839 // process each line
840 for(uint i = 0; i < lines.size(); ++i)
842 std::string line = lines[i];
844 if (line.find("VGA") == std::string::npos &&
845 line.find("3D") == std::string::npos &&
846 line.find("2D") == std::string::npos)
847 continue;
849 std::string::size_type pos = line.find(' ');
851 if (pos == std::string::npos) continue;
853 // found device ID
854 deviceId = line.substr(0, pos);
855 break;
858 if (deviceId.empty())
860 nlwarning("Unable to find a 3D device with lspci");
862 else
864 command = "lspci -v -s " + deviceId;
866 out = getCommandOutput(command);
868 if (out.empty())
870 nlwarning("Unable to launch %s", command.c_str());
872 else
874 explode(out, std::string("\n"), lines, true);
876 // process each line
877 for(uint i = 0; i < lines.size(); ++i)
879 std::string line = lines[i];
881 // look for a size
882 std::string::size_type pos0 = line.find("[size=");
883 if (pos0 == std::string::npos) continue;
885 // move to first digit
886 pos0 += 6;
888 // end of the size
889 std::string::size_type pos1 = line.find("]", pos0);
890 if (pos1 == std::string::npos) continue;
892 sint units;
894 if (line.substr(pos1-1, 1) == "M")
896 // size in MiB
897 units = 1024;
898 --pos1;
900 else if (line.substr(pos1-1, 1) == "K")
902 // size in KiB
903 units = 1;
904 --pos1;
906 else
908 // size in B
909 units = 0;
912 // extract the size
913 std::string sizeStr = line.substr(pos0, pos1-pos0);
915 // convert size to integer with right units
916 sint tmpSize;
917 if (!NLMISC::fromString(sizeStr, tmpSize)) continue;
919 tmpSize *= units;
921 // take the higher size (up to 256 MiB apparently)
922 if (tmpSize > res) res = tmpSize;
925 nlinfo("lspci reported %d KiB of video memory", res);
930 #endif
932 return res;
935 } // NLMISC