From 3ac876a5708ff297f94a370ed0cd57c60cd7518a Mon Sep 17 00:00:00 2001 From: Kamil Hornicek Date: Sun, 19 Mar 2017 22:20:51 +0000 Subject: [PATCH] [OPENGLCFG] - Add a simple applet to set a custom OGL ICD or force the use of the built in software implementation. - It can be used to set the DEBUGCHANNEL to +opengl,+wgl to make obtaining debug info more user friendly. svn path=/trunk/; revision=74208 --- reactos/dll/cpl/CMakeLists.txt | 1 + reactos/dll/cpl/openglcfg/CMakeLists.txt | 19 ++ reactos/dll/cpl/openglcfg/general.c | 197 ++++++++++++++++++ reactos/dll/cpl/openglcfg/lang/en-US.rc | 28 +++ reactos/dll/cpl/openglcfg/openglcfg.c | 78 +++++++ reactos/dll/cpl/openglcfg/openglcfg.h | 30 +++ reactos/dll/cpl/openglcfg/openglcfg.rc | 23 ++ reactos/dll/cpl/openglcfg/openglcfg.spec | 2 + reactos/dll/cpl/openglcfg/resource.h | 20 ++ .../dll/cpl/openglcfg/resources/openglcfg.ico | Bin 0 -> 25214 bytes 10 files changed, 398 insertions(+) create mode 100644 reactos/dll/cpl/openglcfg/CMakeLists.txt create mode 100644 reactos/dll/cpl/openglcfg/general.c create mode 100644 reactos/dll/cpl/openglcfg/lang/en-US.rc create mode 100644 reactos/dll/cpl/openglcfg/openglcfg.c create mode 100644 reactos/dll/cpl/openglcfg/openglcfg.h create mode 100644 reactos/dll/cpl/openglcfg/openglcfg.rc create mode 100644 reactos/dll/cpl/openglcfg/openglcfg.spec create mode 100644 reactos/dll/cpl/openglcfg/resource.h create mode 100644 reactos/dll/cpl/openglcfg/resources/openglcfg.ico diff --git a/reactos/dll/cpl/CMakeLists.txt b/reactos/dll/cpl/CMakeLists.txt index 70a79d3e5ea..fcb32a61012 100644 --- a/reactos/dll/cpl/CMakeLists.txt +++ b/reactos/dll/cpl/CMakeLists.txt @@ -13,6 +13,7 @@ add_subdirectory(main) add_subdirectory(mmsys) add_subdirectory(ncpa) add_subdirectory(odbccp32) +add_subdirectory(openglcfg) add_subdirectory(powercfg) add_subdirectory(sysdm) add_subdirectory(telephon) diff --git a/reactos/dll/cpl/openglcfg/CMakeLists.txt b/reactos/dll/cpl/openglcfg/CMakeLists.txt new file mode 100644 index 00000000000..2018e5b8312 --- /dev/null +++ b/reactos/dll/cpl/openglcfg/CMakeLists.txt @@ -0,0 +1,19 @@ + +spec2def(openglcfg.cpl openglcfg.spec) + +list(APPEND SOURCE + openglcfg.c + general.c + openglcfg.h) + +add_rc_deps(openglcfg.rc ${CMAKE_CURRENT_SOURCE_DIR}/resources/openglcfg.ico) + +add_library(openglcfg SHARED + ${SOURCE} + openglcfg.rc + ${CMAKE_CURRENT_BINARY_DIR}/openglcfg.def) + +set_module_type(openglcfg cpl UNICODE) +add_importlibs(openglcfg user32 comctl32 advapi32 msvcrt kernel32) +add_pch(openglcfg openglcfg.h SOURCE) +add_cd_file(TARGET openglcfg DESTINATION reactos/system32 FOR all) diff --git a/reactos/dll/cpl/openglcfg/general.c b/reactos/dll/cpl/openglcfg/general.c new file mode 100644 index 00000000000..dcf53f55bfd --- /dev/null +++ b/reactos/dll/cpl/openglcfg/general.c @@ -0,0 +1,197 @@ +#include "openglcfg.h" + +#include +#include + +static PWCHAR *pOglDrivers = NULL; +static DWORD dwNumDrivers = 0; + +static VOID InitSettings(HWND hWndDlg) +{ + HKEY hKeyRenderer; + HKEY hKeyDrivers; + WCHAR szBuffer[MAX_KEY_LENGTH]; + WCHAR szBultin[MAX_KEY_LENGTH]; + WCHAR szDriver[MAX_KEY_LENGTH]; + DWORD dwType = 0; + DWORD dwSize = MAX_KEY_LENGTH; + + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, KEY_DRIVERS, 0, KEY_READ, &hKeyDrivers) != ERROR_SUCCESS) + return; + + if (RegCreateKeyExW(HKEY_CURRENT_USER, KEY_RENDERER, 0, NULL, 0, MAXIMUM_ALLOWED, NULL, &hKeyRenderer, NULL) != ERROR_SUCCESS) + { + RegCloseKey(hKeyDrivers); + return; + } + + LoadString(hApplet, IDS_DEBUG_DNM, (LPTSTR)szBultin, 127); + SendDlgItemMessageW(hWndDlg, IDC_DEBUG_OUTPUT, CB_ADDSTRING, 0, (LPARAM)szBultin); + + LoadString(hApplet, IDS_DEBUG_SET, (LPTSTR)szBultin, 127); + SendDlgItemMessageW(hWndDlg, IDC_DEBUG_OUTPUT, CB_ADDSTRING, 0, (LPARAM)szBultin); + + LoadString(hApplet, IDS_DEBUG_CLEAR, (LPTSTR)szBultin, 127); + SendDlgItemMessageW(hWndDlg, IDC_DEBUG_OUTPUT, CB_ADDSTRING, 0, (LPARAM)szBultin); + + SendDlgItemMessageW(hWndDlg, IDC_DEBUG_OUTPUT, CB_SETCURSEL, 0, 0); + + LoadString(hApplet, IDS_RENDERER_DEFAULT, (LPTSTR)szBultin, 127); + SendDlgItemMessageW(hWndDlg, IDC_RENDERER, CB_ADDSTRING, 0, (LPARAM)szBultin); + + LoadString(hApplet, IDS_RENDERER_RSWR, (LPTSTR)szBultin, 127); + SendDlgItemMessageW(hWndDlg, IDC_RENDERER, CB_ADDSTRING, 0, (LPARAM)szBultin); + + if (RegQueryValueExW(hKeyRenderer, NULL, NULL, &dwType, (LPBYTE)szDriver, &dwSize) != ERROR_SUCCESS || dwSize == sizeof(WCHAR)) + SendDlgItemMessageW(hWndDlg, IDC_RENDERER, CB_SETCURSEL, RENDERER_DEFAULT, 0); + + if (dwType == REG_SZ) + { + DWORD ret; + INT iKey; + + if (wcsncmp(szBultin, szDriver, MAX_KEY_LENGTH) == 0) + SendDlgItemMessageW(hWndDlg, IDC_RENDERER, CB_SETCURSEL, RENDERER_RSWR, 0); + + ret = RegQueryInfoKeyW(hKeyDrivers, NULL, NULL, NULL, &dwNumDrivers, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + + if (ret != ERROR_SUCCESS || dwNumDrivers <= 0) + { + RegCloseKey(hKeyDrivers); + RegCloseKey(hKeyRenderer); + return; + } + + pOglDrivers = HeapAlloc(GetProcessHeap(), 0, dwNumDrivers * sizeof(PWCHAR)); + + if (!pOglDrivers) + dwNumDrivers = 0; + + for (iKey = 0; iKey < dwNumDrivers; iKey++) + { + dwSize = MAX_KEY_LENGTH; + ret = RegEnumKeyEx(hKeyDrivers, iKey, szBuffer, &dwSize, NULL, NULL, NULL, NULL); + + if (ret != ERROR_SUCCESS) + break; + + /* Mind the null terminator */ + dwSize++; + + pOglDrivers[iKey] = HeapAlloc(GetProcessHeap(), 0, dwSize * sizeof(WCHAR)); + + if (!pOglDrivers[iKey]) + break; + + SendDlgItemMessageW(hWndDlg, IDC_RENDERER, CB_ADDSTRING, 0, (LPARAM)szBuffer); + + StringCchCopy(pOglDrivers[iKey], dwSize, szBuffer); + + if (wcsncmp(szBuffer, szDriver, MAX_KEY_LENGTH) == 0) + SendDlgItemMessageW(hWndDlg, IDC_RENDERER, CB_SETCURSEL, iKey + 2, 0); + } + } + + RegCloseKey(hKeyDrivers); + RegCloseKey(hKeyRenderer); + + return; +} + +static VOID SaveSettings(HWND hWndDlg) +{ + HKEY hKeyRenderer; + HKEY hKeyDebug; + INT iSel = 0; + + if (RegOpenKeyExW(HKEY_CURRENT_USER, KEY_RENDERER, 0, KEY_WRITE, &hKeyRenderer) != ERROR_SUCCESS) + return; + + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, KEY_DEBUG_CHANNEL, 0, KEY_WRITE, &hKeyDebug) == ERROR_SUCCESS) + { + iSel = (INT)SendDlgItemMessageW(hWndDlg, IDC_DEBUG_OUTPUT, CB_GETCURSEL, 0, 0); + + switch (iSel) + { + case DEBUG_SET: + RegSetValueExW(hKeyDebug, L"DEBUGCHANNEL", 0, REG_SZ, (PBYTE)L"+opengl,+wgl", 13 * sizeof(WCHAR)); + break; + + case DEBUG_CLEAR: + RegSetValueExW(hKeyDebug, L"DEBUGCHANNEL", 0, REG_SZ, (PBYTE)L"", sizeof(WCHAR)); + break; + } + RegCloseKey(hKeyDebug); + } + + iSel = (INT)SendDlgItemMessageW(hWndDlg, IDC_RENDERER, CB_GETCURSEL, 0, 0); + + switch (iSel) + { + case CB_ERR: + break; + + case RENDERER_DEFAULT: + RegSetValueExW(hKeyRenderer, L"", 0, REG_SZ, (PBYTE)L"", sizeof(WCHAR)); + break; + + case RENDERER_RSWR: + { + WCHAR szBuffer[MAX_KEY_LENGTH]; + LoadString(hApplet, IDS_RENDERER_RSWR, (LPTSTR)szBuffer, 127); + RegSetValueExW(hKeyRenderer, L"", 0, REG_SZ, (PBYTE)szBuffer, (wcslen(szBuffer) + 1) * sizeof(WCHAR)); + break; + } + + default: + { + /* Adjustment for DEFAULT and RSWR renderers */ + iSel -= 2; + + if (iSel >= 0 && iSel <= dwNumDrivers) + RegSetValueExW(hKeyRenderer, L"", 0, REG_SZ, (PBYTE)pOglDrivers[iSel], (wcslen(pOglDrivers[iSel]) + 1) * sizeof(WCHAR)); + + break; + } + } + + RegCloseKey(hKeyRenderer); +} + + +INT_PTR CALLBACK GeneralPageProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + LPPSHNOTIFY lppsn; + + switch (uMsg) + { + case WM_INITDIALOG: + InitSettings(hWndDlg); + return TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDC_RENDERER || IDC_DEBUG_OUTPUT) + PropSheet_Changed(GetParent(hWndDlg), hWndDlg); + break; + + case WM_NOTIFY: + lppsn = (LPPSHNOTIFY)lParam; + if (lppsn->hdr.code == PSN_APPLY) + { + SaveSettings(hWndDlg); + return TRUE; + } + break; + + case WM_DESTROY: + { + INT iKey; + for (iKey = 0; iKey <= dwNumDrivers; iKey++) + HeapFree(GetProcessHeap(), 0, pOglDrivers[iKey]); + + HeapFree(GetProcessHeap(), 0, pOglDrivers); + } + } + + return FALSE; +} diff --git a/reactos/dll/cpl/openglcfg/lang/en-US.rc b/reactos/dll/cpl/openglcfg/lang/en-US.rc new file mode 100644 index 00000000000..29601e0d9bf --- /dev/null +++ b/reactos/dll/cpl/openglcfg/lang/en-US.rc @@ -0,0 +1,28 @@ +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +IDD_PROPPAGEGENERAL DIALOGEX 0, 0, 246, 228 +STYLE DS_SHELLFONT | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "General" +FONT 8, "MS Shell Dlg" +BEGIN + ICON IDI_CPLICON, IDI_CPLICON, 8, 0, 21, 20 + GROUPBOX "Renderer settings", -1, 5, 25, 230, 90 + LTEXT "&Renderer:", -1, 15, 42, 37, 10 + COMBOBOX IDC_RENDERER, 55, 40, 160, 10, WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST + LTEXT "Choose a system wide renderer. This will override the default settings for your graphics driver! Choose ""Default"" if you want to go back to your original settings. Choose ""ReactOS Software Implementation"" if you want to use the built-in software renderer.", -1, 15, 60, 210, 40 + GROUPBOX "Debugging", -1, 5, 125, 230, 90 + LTEXT "DEBUGCHANNEL:", -1, 15, 142, 65, 10 + COMBOBOX IDC_DEBUG_OUTPUT, 80, 140, 135, 10, WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST + LTEXT "This will set the DEBUGCHANNEL environment variable to enable/disable debug output from opengl32.\n\nWARNING: Choosing ""Set"" or ""Clear"" will overwrite the content of the DEBUGCHANNEL environment variable!", -1, 15, 165, 210, 40 +END + +STRINGTABLE +BEGIN + IDS_RENDERER_DEFAULT "System default" + IDS_RENDERER_RSWR "ReactOS Software Implementation" + IDS_CPLNAME "OpenGL configuration" + IDS_CPLDESCRIPTION "Configures OpenGL renderer." + IDS_DEBUG_SET "set" + IDS_DEBUG_CLEAR "clear" + IDS_DEBUG_DNM "do not modify" +END diff --git a/reactos/dll/cpl/openglcfg/openglcfg.c b/reactos/dll/cpl/openglcfg/openglcfg.c new file mode 100644 index 00000000000..8a142fdf409 --- /dev/null +++ b/reactos/dll/cpl/openglcfg/openglcfg.c @@ -0,0 +1,78 @@ +#include "openglcfg.h" + +#include + +HINSTANCE hApplet = 0; + +LONG CALLBACK AppletInit(HWND hWnd) +{ + PROPSHEETPAGEW psp; + PROPSHEETHEADERW psh; + WCHAR szCaption[1024]; + + LoadStringW(hApplet, IDS_CPLNAME, szCaption, sizeof(szCaption) / sizeof(WCHAR)); + + ZeroMemory(&psp, sizeof(PROPSHEETPAGE)); + psp.dwSize = sizeof(PROPSHEETPAGE); + psp.dwFlags = PSP_DEFAULT; + psp.hInstance = hApplet; + psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGEGENERAL); + psp.pfnDlgProc = GeneralPageProc; + + ZeroMemory(&psh, sizeof(PROPSHEETHEADER)); + psh.dwSize = sizeof(PROPSHEETHEADER); + psh.dwFlags = PSH_PROPSHEETPAGE; + psh.hwndParent = hWnd; + psh.hInstance = hApplet; + psh.hIcon = LoadIcon(hApplet, MAKEINTRESOURCE(IDI_CPLICON)); + psh.pszCaption = szCaption; + psh.nPages = sizeof(psp) / sizeof(PROPSHEETPAGE); + psh.nStartPage = 0; + psh.ppsp = &psp; + + return (LONG)(PropertySheet(&psh) != -1); +} + +LONG CALLBACK CPlApplet(HWND hWnd, UINT uMsg, LPARAM lParam1, LPARAM lParam2) +{ + switch (uMsg) + { + case CPL_INIT: + return TRUE; + + case CPL_GETCOUNT: + return 1; + + case CPL_INQUIRE: + { + CPLINFO *CPlInfo = (CPLINFO*)lParam2; + CPlInfo->lData = 0; + CPlInfo->idIcon = IDI_CPLICON; + CPlInfo->idInfo = IDS_CPLDESCRIPTION; + CPlInfo->idName = IDS_CPLNAME; + } + break; + + case CPL_DBLCLK: + AppletInit(hWnd); + break; + } + + return FALSE; +} + + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved) +{ + UNREFERENCED_PARAMETER(lpvReserved); + + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + hApplet = hinstDLL; + break; + } + + return TRUE; +} diff --git a/reactos/dll/cpl/openglcfg/openglcfg.h b/reactos/dll/cpl/openglcfg/openglcfg.h new file mode 100644 index 00000000000..aef622c3963 --- /dev/null +++ b/reactos/dll/cpl/openglcfg/openglcfg.h @@ -0,0 +1,30 @@ +#ifndef _OPENGLCFG_PCH_ +#define _OPENGLCFG_PCH_ + +#include +#define WIN32_NO_STATUS +#include +#include +#include +#include +#include + +#include "resource.h" + +#define MAX_KEY_LENGTH 256 + +#define RENDERER_DEFAULT 0 +#define RENDERER_RSWR 1 + +#define DEBUG_SET 1 +#define DEBUG_CLEAR 2 + +#define KEY_RENDERER L"Software\\ReactOS\\OpenGL" +#define KEY_DRIVERS L"Software\\Microsoft\\Windows NT\\CurrentVersion\\OpenGLDrivers" +#define KEY_DEBUG_CHANNEL L"System\\CurrentControlSet\\Control\\Session Manager\\Environment" + +INT_PTR CALLBACK GeneralPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + +extern HINSTANCE hApplet; + +#endif /* _OPENGLCFG_PCH_ */ diff --git a/reactos/dll/cpl/openglcfg/openglcfg.rc b/reactos/dll/cpl/openglcfg/openglcfg.rc new file mode 100644 index 00000000000..7e20d700206 --- /dev/null +++ b/reactos/dll/cpl/openglcfg/openglcfg.rc @@ -0,0 +1,23 @@ +#include +#include + +#include "resource.h" + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "ReactOS OpenGL Configuration Control Panel" +#define REACTOS_STR_INTERNAL_NAME "OpenGLcfg" +#define REACTOS_STR_ORIGINAL_FILENAME "OpenGLcfg.cpl" +#include + +IDI_CPLICON ICON "resources/openglcfg.ico" + +#include + +/* UTF-8 */ +#pragma code_page(65001) + +#ifdef LANGUAGE_EN_US + #include "lang/en-US.rc" +#endif diff --git a/reactos/dll/cpl/openglcfg/openglcfg.spec b/reactos/dll/cpl/openglcfg/openglcfg.spec new file mode 100644 index 00000000000..93898831339 --- /dev/null +++ b/reactos/dll/cpl/openglcfg/openglcfg.spec @@ -0,0 +1,2 @@ +@ stdcall CPlApplet(ptr long ptr ptr) + diff --git a/reactos/dll/cpl/openglcfg/resource.h b/reactos/dll/cpl/openglcfg/resource.h new file mode 100644 index 00000000000..af91aa5ed58 --- /dev/null +++ b/reactos/dll/cpl/openglcfg/resource.h @@ -0,0 +1,20 @@ +#pragma once + +/* Icons */ +#define IDI_CPLICON 1 + +/* Dialogs */ +#define IDD_PROPPAGEGENERAL 100 + +/* Controls */ +#define IDC_RENDERER 1001 +#define IDC_DEBUG_OUTPUT 1002 + +/* Strings */ +#define IDS_CPLNAME 10000 +#define IDS_CPLDESCRIPTION 10001 +#define IDS_RENDERER_DEFAULT 10002 +#define IDS_RENDERER_RSWR 10003 +#define IDS_DEBUG_SET 10004 +#define IDS_DEBUG_CLEAR 10005 +#define IDS_DEBUG_DNM 10006 diff --git a/reactos/dll/cpl/openglcfg/resources/openglcfg.ico b/reactos/dll/cpl/openglcfg/resources/openglcfg.ico new file mode 100644 index 0000000000000000000000000000000000000000..168637c52680f467165f646d8a818c592aa6e0ef GIT binary patch literal 25214 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