[REACTOS]
[reactos.git] / reactos / win32ss / user / consrv / settings.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: win32ss/user/consrv/settings.c
5 * PURPOSE: Console settings management
6 * PROGRAMMERS: Johannes Anderwald
7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
8 */
9
10 /* INCLUDES *******************************************************************/
11
12 #include "consrv.h"
13 #include "include/conio.h"
14 #include "conio.h"
15 #include "include/settings.h"
16
17 #include <stdio.h> // for swprintf
18
19 #define NDEBUG
20 #include <debug.h>
21
22
23 /* GLOBALS ********************************************************************/
24
25 extern const COLORREF s_Colors[16];
26
27
28 /* FUNCTIONS ******************************************************************/
29
30 static VOID
31 TranslateConsoleName(OUT LPWSTR DestString,
32 IN LPCWSTR ConsoleName,
33 IN UINT MaxStrLen)
34 {
35 #define PATH_SEPARATOR L'\\'
36
37 UINT wLength;
38
39 if ( DestString == NULL || ConsoleName == NULL ||
40 *ConsoleName == L'\0' || MaxStrLen == 0 )
41 {
42 return;
43 }
44
45 wLength = GetWindowsDirectoryW(DestString, MaxStrLen);
46 if ((wLength > 0) && (_wcsnicmp(ConsoleName, DestString, wLength) == 0))
47 {
48 wcsncpy(DestString, L"%SystemRoot%", MaxStrLen);
49 // FIXME: Fix possible buffer overflows there !!!!!
50 wcsncat(DestString, ConsoleName + wLength, MaxStrLen);
51 }
52 else
53 {
54 wcsncpy(DestString, ConsoleName, MaxStrLen);
55 }
56
57 /* Replace path separators (backslashes) by underscores */
58 while ((DestString = wcschr(DestString, PATH_SEPARATOR))) *DestString = L'_';
59 }
60
61 static BOOL
62 OpenUserRegistryPathPerProcessId(DWORD ProcessId,
63 PHKEY hResult,
64 REGSAM samDesired)
65 {
66 BOOL bRet = TRUE;
67 HANDLE hProcessToken = NULL;
68 HANDLE hProcess;
69 BYTE Buffer[256];
70 DWORD Length = 0;
71 UNICODE_STRING SidName;
72 PTOKEN_USER TokUser;
73
74 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | READ_CONTROL, FALSE, ProcessId);
75 if (!hProcess)
76 {
77 DPRINT("Error: OpenProcess failed(0x%x)\n", GetLastError());
78 return FALSE;
79 }
80
81 if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hProcessToken))
82 {
83 DPRINT("Error: OpenProcessToken failed(0x%x)\n", GetLastError());
84 CloseHandle(hProcess);
85 return FALSE;
86 }
87
88 if (!GetTokenInformation(hProcessToken, TokenUser, (PVOID)Buffer, sizeof(Buffer), &Length))
89 {
90 DPRINT("Error: GetTokenInformation failed(0x%x)\n",GetLastError());
91 CloseHandle(hProcessToken);
92 CloseHandle(hProcess);
93 return FALSE;
94 }
95
96 TokUser = ((PTOKEN_USER)Buffer)->User.Sid;
97 if (!NT_SUCCESS(RtlConvertSidToUnicodeString(&SidName, TokUser, TRUE)))
98 {
99 DPRINT("Error: RtlConvertSidToUnicodeString failed(0x%x)\n", GetLastError());
100 CloseHandle(hProcessToken);
101 CloseHandle(hProcess);
102 return FALSE;
103 }
104
105 bRet = (RegOpenKeyExW(HKEY_USERS,
106 SidName.Buffer,
107 0,
108 samDesired,
109 hResult) == ERROR_SUCCESS);
110
111 RtlFreeUnicodeString(&SidName);
112
113 CloseHandle(hProcessToken);
114 CloseHandle(hProcess);
115
116 return bRet;
117 }
118
119 /*static*/ BOOL
120 ConSrvOpenUserSettings(DWORD ProcessId,
121 LPCWSTR ConsoleTitle,
122 PHKEY hSubKey,
123 REGSAM samDesired,
124 BOOL bCreate)
125 {
126 BOOL bRet = TRUE;
127 WCHAR szBuffer[MAX_PATH] = L"Console\\";
128 WCHAR szBuffer2[MAX_PATH] = L"";
129 HKEY hKey;
130
131 /*
132 * Console properties are stored under the HKCU\Console\* key.
133 *
134 * We use the original console title as the subkey name for storing
135 * console properties. We need to distinguish whether we were launched
136 * via the console application directly or via a shortcut.
137 *
138 * If the title of the console corresponds to a path (more precisely,
139 * if the title is of the form: C:\ReactOS\<some_path>\<some_app.exe>),
140 * then use the corresponding unexpanded path and with the backslashes
141 * replaced by underscores, to make the registry happy,
142 * i.e. %SystemRoot%_<some_path>_<some_app.exe>
143 */
144
145 /* Open the registry key where we saved the console properties */
146 if (!OpenUserRegistryPathPerProcessId(ProcessId, &hKey, samDesired))
147 {
148 DPRINT1("OpenUserRegistryPathPerProcessId failed\n");
149 return FALSE;
150 }
151
152 /*
153 * Try to open properties via the console title:
154 * to make the registry happy, replace all the
155 * backslashes by underscores.
156 */
157 TranslateConsoleName(szBuffer2, ConsoleTitle, MAX_PATH);
158
159 /* Create the registry path */
160 wcsncat(szBuffer, szBuffer2, MAX_PATH);
161
162 /* Create or open the registry key */
163 if (bCreate)
164 {
165 /* Create the key */
166 bRet = (RegCreateKeyExW(hKey,
167 szBuffer,
168 0, NULL,
169 REG_OPTION_NON_VOLATILE,
170 samDesired,
171 NULL,
172 hSubKey,
173 NULL) == ERROR_SUCCESS);
174 }
175 else
176 {
177 /* Open the key */
178 bRet = (RegOpenKeyExW(hKey,
179 szBuffer,
180 0,
181 samDesired,
182 hSubKey) == ERROR_SUCCESS);
183 }
184
185 /* Close the parent key and return success or not */
186 RegCloseKey(hKey);
187 return bRet;
188 }
189
190 BOOL
191 ConSrvReadUserSettings(IN OUT PCONSOLE_INFO ConsoleInfo,
192 IN DWORD ProcessId)
193 {
194 BOOL RetVal = FALSE;
195 HKEY hKey;
196 DWORD dwNumSubKeys = 0;
197 DWORD dwIndex;
198 DWORD dwColorIndex = 0;
199 DWORD dwType;
200 WCHAR szValueName[MAX_PATH];
201 DWORD dwValueName;
202 WCHAR szValue[LF_FACESIZE] = L"\0";
203 DWORD Value;
204 DWORD dwValue;
205
206 if (!ConSrvOpenUserSettings(ProcessId,
207 ConsoleInfo->ConsoleTitle,
208 &hKey, KEY_READ,
209 FALSE))
210 {
211 DPRINT("ConSrvOpenUserSettings failed\n");
212 return FALSE;
213 }
214
215 if (RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL,
216 &dwNumSubKeys, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
217 {
218 DPRINT("ConSrvReadUserSettings: RegQueryInfoKey failed\n");
219 RegCloseKey(hKey);
220 return FALSE;
221 }
222
223 DPRINT("ConSrvReadUserSettings entered dwNumSubKeys %d\n", dwNumSubKeys);
224
225 for (dwIndex = 0; dwIndex < dwNumSubKeys; dwIndex++)
226 {
227 dwValue = sizeof(Value);
228 dwValueName = MAX_PATH; // sizeof(szValueName)/sizeof(szValueName[0])
229
230 if (RegEnumValueW(hKey, dwIndex, szValueName, &dwValueName, NULL, &dwType, (BYTE*)&Value, &dwValue) != ERROR_SUCCESS)
231 {
232 if (dwType == REG_SZ)
233 {
234 /*
235 * Retry in case of string value
236 */
237 dwValue = sizeof(szValue);
238 dwValueName = MAX_PATH; // sizeof(szValueName)/sizeof(szValueName[0])
239 if (RegEnumValueW(hKey, dwIndex, szValueName, &dwValueName, NULL, NULL, (BYTE*)szValue, &dwValue) != ERROR_SUCCESS)
240 break;
241 }
242 else
243 {
244 break;
245 }
246 }
247
248 /* Maybe it is UI-specific ?? */
249 if (!wcsncmp(szValueName, L"ColorTable", wcslen(L"ColorTable")))
250 {
251 dwColorIndex = 0;
252 swscanf(szValueName, L"ColorTable%2d", &dwColorIndex);
253 if (dwColorIndex < sizeof(ConsoleInfo->Colors)/sizeof(ConsoleInfo->Colors[0]))
254 {
255 ConsoleInfo->Colors[dwColorIndex] = Value;
256 RetVal = TRUE;
257 }
258 }
259 else if (!wcscmp(szValueName, L"HistoryBufferSize"))
260 {
261 ConsoleInfo->HistoryBufferSize = Value;
262 RetVal = TRUE;
263 }
264 else if (!wcscmp(szValueName, L"NumberOfHistoryBuffers"))
265 {
266 ConsoleInfo->NumberOfHistoryBuffers = Value;
267 RetVal = TRUE;
268 }
269 else if (!wcscmp(szValueName, L"HistoryNoDup"))
270 {
271 ConsoleInfo->HistoryNoDup = (BOOLEAN)Value;
272 RetVal = TRUE;
273 }
274 else if (!wcscmp(szValueName, L"FullScreen"))
275 {
276 ConsoleInfo->FullScreen = Value;
277 RetVal = TRUE;
278 }
279 else if (!wcscmp(szValueName, L"QuickEdit"))
280 {
281 ConsoleInfo->QuickEdit = (BOOLEAN)Value;
282 RetVal = TRUE;
283 }
284 else if (!wcscmp(szValueName, L"InsertMode"))
285 {
286 ConsoleInfo->InsertMode = (BOOLEAN)Value;
287 RetVal = TRUE;
288 }
289 else if (!wcscmp(szValueName, L"ScreenBufferSize"))
290 {
291 ConsoleInfo->ScreenBufferSize.X = LOWORD(Value);
292 ConsoleInfo->ScreenBufferSize.Y = HIWORD(Value);
293 RetVal = TRUE;
294 }
295 else if (!wcscmp(szValueName, L"WindowSize"))
296 {
297 ConsoleInfo->ConsoleSize.X = LOWORD(Value);
298 ConsoleInfo->ConsoleSize.Y = HIWORD(Value);
299 RetVal = TRUE;
300 }
301 else if (!wcscmp(szValueName, L"CursorSize"))
302 {
303 ConsoleInfo->CursorSize = min(max(Value, 0), 100);
304 RetVal = TRUE;
305 }
306 else if (!wcscmp(szValueName, L"ScreenColors"))
307 {
308 ConsoleInfo->ScreenAttrib = (USHORT)Value;
309 RetVal = TRUE;
310 }
311 else if (!wcscmp(szValueName, L"PopupColors"))
312 {
313 ConsoleInfo->PopupAttrib = (USHORT)Value;
314 RetVal = TRUE;
315 }
316 }
317
318 RegCloseKey(hKey);
319 return RetVal;
320 }
321
322 BOOL
323 ConSrvWriteUserSettings(IN PCONSOLE_INFO ConsoleInfo,
324 IN DWORD ProcessId)
325 {
326 BOOL GlobalSettings = (ConsoleInfo->ConsoleTitle[0] == L'\0');
327 HKEY hKey;
328 DWORD Storage = 0;
329
330 #define SetConsoleSetting(SettingName, SettingType, SettingSize, Setting, DefaultValue) \
331 do { \
332 if (GlobalSettings || (!GlobalSettings && (*(Setting) != (DefaultValue)))) \
333 { \
334 RegSetValueExW(hKey, (SettingName), 0, (SettingType), (PBYTE)(Setting), (SettingSize)); \
335 } \
336 else \
337 { \
338 RegDeleteValue(hKey, (SettingName)); \
339 } \
340 } while (0)
341
342 WCHAR szValueName[15];
343 UINT i;
344
345 if (!ConSrvOpenUserSettings(ProcessId,
346 ConsoleInfo->ConsoleTitle,
347 &hKey, KEY_WRITE,
348 TRUE))
349 {
350 return FALSE;
351 }
352
353 for (i = 0 ; i < sizeof(ConsoleInfo->Colors)/sizeof(ConsoleInfo->Colors[0]) ; ++i)
354 {
355 /*
356 * Write only the new value if we are saving the global settings
357 * or we are saving settings for a particular console, which differs
358 * from the default ones.
359 */
360 swprintf(szValueName, L"ColorTable%02d", i);
361 SetConsoleSetting(szValueName, REG_DWORD, sizeof(DWORD), &ConsoleInfo->Colors[i], s_Colors[i]);
362 }
363
364 SetConsoleSetting(L"HistoryBufferSize", REG_DWORD, sizeof(DWORD), &ConsoleInfo->HistoryBufferSize, 50);
365 SetConsoleSetting(L"NumberOfHistoryBuffers", REG_DWORD, sizeof(DWORD), &ConsoleInfo->NumberOfHistoryBuffers, 4);
366
367 Storage = ConsoleInfo->HistoryNoDup;
368 SetConsoleSetting(L"HistoryNoDup", REG_DWORD, sizeof(DWORD), &Storage, FALSE);
369
370 Storage = ConsoleInfo->FullScreen;
371 SetConsoleSetting(L"FullScreen", REG_DWORD, sizeof(DWORD), &Storage, FALSE);
372
373 Storage = ConsoleInfo->QuickEdit;
374 SetConsoleSetting(L"QuickEdit", REG_DWORD, sizeof(DWORD), &Storage, FALSE);
375
376 Storage = ConsoleInfo->InsertMode;
377 SetConsoleSetting(L"InsertMode", REG_DWORD, sizeof(DWORD), &Storage, TRUE);
378
379 Storage = MAKELONG(ConsoleInfo->ScreenBufferSize.X, ConsoleInfo->ScreenBufferSize.Y);
380 SetConsoleSetting(L"ScreenBufferSize", REG_DWORD, sizeof(DWORD), &Storage, MAKELONG(80, 300));
381
382 Storage = MAKELONG(ConsoleInfo->ConsoleSize.X, ConsoleInfo->ConsoleSize.Y);
383 SetConsoleSetting(L"WindowSize", REG_DWORD, sizeof(DWORD), &Storage, MAKELONG(80, 25));
384
385 SetConsoleSetting(L"CursorSize", REG_DWORD, sizeof(DWORD), &ConsoleInfo->CursorSize, CSR_DEFAULT_CURSOR_SIZE);
386
387 Storage = ConsoleInfo->ScreenAttrib;
388 SetConsoleSetting(L"ScreenColors", REG_DWORD, sizeof(DWORD), &Storage, DEFAULT_SCREEN_ATTRIB);
389
390 Storage = ConsoleInfo->PopupAttrib;
391 SetConsoleSetting(L"PopupColors", REG_DWORD, sizeof(DWORD), &Storage, DEFAULT_POPUP_ATTRIB);
392
393 RegCloseKey(hKey);
394 return TRUE;
395 }
396
397 VOID
398 ConSrvGetDefaultSettings(IN OUT PCONSOLE_INFO ConsoleInfo,
399 IN DWORD ProcessId)
400 {
401 if (ConsoleInfo == NULL) return;
402
403 /// HKCU,"Console","LoadConIme",0x00010003,1
404
405 /*
406 * 1. Load the default values
407 */
408 // #define DEFAULT_HISTORY_COMMANDS_NUMBER 50
409 // #define DEFAULT_HISTORY_BUFFERS_NUMBER 4
410 ConsoleInfo->HistoryBufferSize = 50;
411 ConsoleInfo->NumberOfHistoryBuffers = 4;
412 ConsoleInfo->HistoryNoDup = FALSE;
413
414 ConsoleInfo->FullScreen = FALSE;
415 ConsoleInfo->QuickEdit = FALSE;
416 ConsoleInfo->InsertMode = TRUE;
417 // ConsoleInfo->InputBufferSize;
418 ConsoleInfo->ScreenBufferSize.X = 80;
419 ConsoleInfo->ScreenBufferSize.Y = 300;
420 ConsoleInfo->ConsoleSize.X = 80;
421 ConsoleInfo->ConsoleSize.Y = 25;
422
423 ConsoleInfo->CursorBlinkOn;
424 ConsoleInfo->ForceCursorOff;
425 ConsoleInfo->CursorSize = CSR_DEFAULT_CURSOR_SIZE; // #define SMALL_SIZE 25
426
427 ConsoleInfo->ScreenAttrib = DEFAULT_SCREEN_ATTRIB;
428 ConsoleInfo->PopupAttrib = DEFAULT_POPUP_ATTRIB;
429
430 memcpy(ConsoleInfo->Colors, s_Colors, sizeof(s_Colors));
431
432 // ConsoleInfo->CodePage;
433
434 ConsoleInfo->ConsoleTitle[0] = L'\0';
435
436 /*
437 * 2. Overwrite them with the ones stored in HKCU\Console.
438 * If the HKCU\Console key doesn't exist, create it
439 * and store the default values inside.
440 */
441 if (!ConSrvReadUserSettings(ConsoleInfo, ProcessId))
442 {
443 ConSrvWriteUserSettings(ConsoleInfo, ProcessId);
444 }
445 }
446
447 VOID
448 ConSrvApplyUserSettings(IN PCONSOLE Console,
449 IN PCONSOLE_INFO ConsoleInfo)
450 {
451 PCONSOLE_SCREEN_BUFFER ActiveBuffer = Console->ActiveBuffer;
452 COORD BufSize;
453 BOOL SizeChanged = FALSE;
454
455 /*
456 * Apply full-screen mode.
457 */
458 if (ConsoleInfo->FullScreen)
459 Console->ActiveBuffer->DisplayMode |= CONSOLE_FULLSCREEN_MODE;
460 else
461 Console->ActiveBuffer->DisplayMode &= ~CONSOLE_FULLSCREEN_MODE;
462 // TODO: Apply it really
463
464 /*
465 * Apply terminal-edition settings:
466 * - QuickEdit and Insert modes,
467 * - history settings.
468 */
469 Console->QuickEdit = ConsoleInfo->QuickEdit;
470 Console->InsertMode = ConsoleInfo->InsertMode;
471
472 /*
473 * Apply foreground and background colors for both screen and popup
474 * and copy the new palette.
475 */
476 ActiveBuffer->ScreenDefaultAttrib = ConsoleInfo->ScreenAttrib;
477 ActiveBuffer->PopupDefaultAttrib = ConsoleInfo->PopupAttrib;
478 memcpy(Console->Colors, ConsoleInfo->Colors, sizeof(s_Colors)); // FIXME: Possible buffer overflow if s_colors is bigger than pConInfo->Colors.
479
480 // TODO: Really update the screen attributes as FillConsoleOutputAttribute does.
481
482 /* Apply cursor size */
483 ActiveBuffer->CursorInfo.bVisible = (ConsoleInfo->CursorSize != 0);
484 ActiveBuffer->CursorInfo.dwSize = min(max(ConsoleInfo->CursorSize, 0), 100);
485
486 /* Resize the console */
487 if (ConsoleInfo->ConsoleSize.X != Console->ConsoleSize.X ||
488 ConsoleInfo->ConsoleSize.Y != Console->ConsoleSize.Y)
489 {
490 Console->ConsoleSize = ConsoleInfo->ConsoleSize;
491 SizeChanged = TRUE;
492 }
493
494 /* Resize its active screen-buffer */
495 BufSize = ConsoleInfo->ScreenBufferSize;
496 if (BufSize.X != ActiveBuffer->ScreenBufferSize.X ||
497 BufSize.Y != ActiveBuffer->ScreenBufferSize.Y)
498 {
499 if (NT_SUCCESS(ConioResizeBuffer(Console, ActiveBuffer, BufSize)))
500 SizeChanged = TRUE;
501 }
502
503 if (SizeChanged) ConioResizeTerminal(Console);
504 }
505
506 /* EOF */