738d5bfce3c98fb27338b657e327060e86bdea98
[reactos.git] / win32ss / user / winsrv / consrv_new / settings.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: win32ss/user/winsrv/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 "include/conio2.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 DPRINT1("Error: OpenProcess failed(0x%x)\n", GetLastError());
78 return FALSE;
79 }
80
81 if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hProcessToken))
82 {
83 DPRINT1("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 DPRINT1("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 DPRINT1("Error: RtlConvertSidToUnicodeString failed(0x%x)\n", GetLastError());
100 CloseHandle(hProcessToken);
101 CloseHandle(hProcess);
102 return FALSE;
103 }
104
105 /*
106 * Might fail for LiveCD... Why ? Because only HKU\.DEFAULT exists.
107 */
108 bRet = (RegOpenKeyExW(HKEY_USERS,
109 SidName.Buffer,
110 0,
111 samDesired,
112 hResult) == ERROR_SUCCESS);
113
114 RtlFreeUnicodeString(&SidName);
115
116 CloseHandle(hProcessToken);
117 CloseHandle(hProcess);
118
119 return bRet;
120 }
121
122 /*static*/ BOOL
123 ConSrvOpenUserSettings(DWORD ProcessId,
124 LPCWSTR ConsoleTitle,
125 PHKEY hSubKey,
126 REGSAM samDesired,
127 BOOL bCreate)
128 {
129 BOOL bRet = TRUE;
130 WCHAR szBuffer[MAX_PATH] = L"Console\\";
131 WCHAR szBuffer2[MAX_PATH] = L"";
132 HKEY hKey;
133
134 /*
135 * Console properties are stored under the HKCU\Console\* key.
136 *
137 * We use the original console title as the subkey name for storing
138 * console properties. We need to distinguish whether we were launched
139 * via the console application directly or via a shortcut.
140 *
141 * If the title of the console corresponds to a path (more precisely,
142 * if the title is of the form: C:\ReactOS\<some_path>\<some_app.exe>),
143 * then use the corresponding unexpanded path and with the backslashes
144 * replaced by underscores, to make the registry happy,
145 * i.e. %SystemRoot%_<some_path>_<some_app.exe>
146 */
147
148 /* Open the registry key where we saved the console properties */
149 if (!OpenUserRegistryPathPerProcessId(ProcessId, &hKey, samDesired))
150 {
151 DPRINT1("OpenUserRegistryPathPerProcessId failed\n");
152 return FALSE;
153 }
154
155 /*
156 * Try to open properties via the console title:
157 * to make the registry happy, replace all the
158 * backslashes by underscores.
159 */
160 TranslateConsoleName(szBuffer2, ConsoleTitle, MAX_PATH);
161
162 /* Create the registry path */
163 wcsncat(szBuffer, szBuffer2, MAX_PATH);
164
165 /* Create or open the registry key */
166 if (bCreate)
167 {
168 /* Create the key */
169 bRet = (RegCreateKeyExW(hKey,
170 szBuffer,
171 0, NULL,
172 REG_OPTION_NON_VOLATILE,
173 samDesired,
174 NULL,
175 hSubKey,
176 NULL) == ERROR_SUCCESS);
177 }
178 else
179 {
180 /* Open the key */
181 bRet = (RegOpenKeyExW(hKey,
182 szBuffer,
183 0,
184 samDesired,
185 hSubKey) == ERROR_SUCCESS);
186 }
187
188 /* Close the parent key and return success or not */
189 RegCloseKey(hKey);
190 return bRet;
191 }
192
193 BOOL
194 ConSrvReadUserSettings(IN OUT PCONSOLE_INFO ConsoleInfo,
195 IN DWORD ProcessId)
196 {
197 BOOL RetVal = FALSE;
198 HKEY hKey;
199 DWORD dwNumSubKeys = 0;
200 DWORD dwIndex;
201 DWORD dwColorIndex = 0;
202 DWORD dwType;
203 WCHAR szValueName[MAX_PATH];
204 DWORD dwValueName;
205 WCHAR szValue[LF_FACESIZE] = L"\0";
206 DWORD Value;
207 DWORD dwValue;
208
209 if (!ConSrvOpenUserSettings(ProcessId,
210 ConsoleInfo->ConsoleTitle,
211 &hKey, KEY_READ,
212 FALSE))
213 {
214 DPRINT("ConSrvOpenUserSettings failed\n");
215 return FALSE;
216 }
217
218 if (RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL,
219 &dwNumSubKeys, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
220 {
221 DPRINT("ConSrvReadUserSettings: RegQueryInfoKey failed\n");
222 RegCloseKey(hKey);
223 return FALSE;
224 }
225
226 DPRINT("ConSrvReadUserSettings entered dwNumSubKeys %d\n", dwNumSubKeys);
227
228 for (dwIndex = 0; dwIndex < dwNumSubKeys; dwIndex++)
229 {
230 dwValue = sizeof(Value);
231 dwValueName = MAX_PATH; // sizeof(szValueName)/sizeof(szValueName[0])
232
233 if (RegEnumValueW(hKey, dwIndex, szValueName, &dwValueName, NULL, &dwType, (BYTE*)&Value, &dwValue) != ERROR_SUCCESS)
234 {
235 if (dwType == REG_SZ)
236 {
237 /*
238 * Retry in case of string value
239 */
240 dwValue = sizeof(szValue);
241 dwValueName = MAX_PATH; // sizeof(szValueName)/sizeof(szValueName[0])
242 if (RegEnumValueW(hKey, dwIndex, szValueName, &dwValueName, NULL, NULL, (BYTE*)szValue, &dwValue) != ERROR_SUCCESS)
243 break;
244 }
245 else
246 {
247 break;
248 }
249 }
250
251 /* Maybe it is UI-specific ?? */
252 if (!wcsncmp(szValueName, L"ColorTable", wcslen(L"ColorTable")))
253 {
254 dwColorIndex = 0;
255 swscanf(szValueName, L"ColorTable%2d", &dwColorIndex);
256 if (dwColorIndex < sizeof(ConsoleInfo->Colors)/sizeof(ConsoleInfo->Colors[0]))
257 {
258 ConsoleInfo->Colors[dwColorIndex] = Value;
259 RetVal = TRUE;
260 }
261 }
262 else if (!wcscmp(szValueName, L"HistoryBufferSize"))
263 {
264 ConsoleInfo->HistoryBufferSize = Value;
265 RetVal = TRUE;
266 }
267 else if (!wcscmp(szValueName, L"NumberOfHistoryBuffers"))
268 {
269 ConsoleInfo->NumberOfHistoryBuffers = Value;
270 RetVal = TRUE;
271 }
272 else if (!wcscmp(szValueName, L"HistoryNoDup"))
273 {
274 ConsoleInfo->HistoryNoDup = (BOOLEAN)Value;
275 RetVal = TRUE;
276 }
277 else if (!wcscmp(szValueName, L"QuickEdit"))
278 {
279 ConsoleInfo->QuickEdit = (BOOLEAN)Value;
280 RetVal = TRUE;
281 }
282 else if (!wcscmp(szValueName, L"InsertMode"))
283 {
284 ConsoleInfo->InsertMode = (BOOLEAN)Value;
285 RetVal = TRUE;
286 }
287 else if (!wcscmp(szValueName, L"ScreenBufferSize"))
288 {
289 ConsoleInfo->ScreenBufferSize.X = LOWORD(Value);
290 ConsoleInfo->ScreenBufferSize.Y = HIWORD(Value);
291 RetVal = TRUE;
292 }
293 else if (!wcscmp(szValueName, L"WindowSize"))
294 {
295 ConsoleInfo->ConsoleSize.X = LOWORD(Value);
296 ConsoleInfo->ConsoleSize.Y = HIWORD(Value);
297 RetVal = TRUE;
298 }
299 else if (!wcscmp(szValueName, L"CursorSize"))
300 {
301 ConsoleInfo->CursorSize = min(max(Value, 0), 100);
302 RetVal = TRUE;
303 }
304 else if (!wcscmp(szValueName, L"ScreenColors"))
305 {
306 ConsoleInfo->ScreenAttrib = (USHORT)Value;
307 RetVal = TRUE;
308 }
309 else if (!wcscmp(szValueName, L"PopupColors"))
310 {
311 ConsoleInfo->PopupAttrib = (USHORT)Value;
312 RetVal = TRUE;
313 }
314 }
315
316 RegCloseKey(hKey);
317 return RetVal;
318 }
319
320 BOOL
321 ConSrvWriteUserSettings(IN PCONSOLE_INFO ConsoleInfo,
322 IN DWORD ProcessId)
323 {
324 BOOL GlobalSettings = (ConsoleInfo->ConsoleTitle[0] == L'\0');
325 HKEY hKey;
326 DWORD Storage = 0;
327
328 #define SetConsoleSetting(SettingName, SettingType, SettingSize, Setting, DefaultValue) \
329 do { \
330 if (GlobalSettings || (!GlobalSettings && (*(Setting) != (DefaultValue)))) \
331 { \
332 RegSetValueExW(hKey, (SettingName), 0, (SettingType), (PBYTE)(Setting), (SettingSize)); \
333 } \
334 else \
335 { \
336 RegDeleteValue(hKey, (SettingName)); \
337 } \
338 } while (0)
339
340 WCHAR szValueName[15];
341 UINT i;
342
343 if (!ConSrvOpenUserSettings(ProcessId,
344 ConsoleInfo->ConsoleTitle,
345 &hKey, KEY_WRITE,
346 TRUE))
347 {
348 return FALSE;
349 }
350
351 for (i = 0 ; i < sizeof(ConsoleInfo->Colors)/sizeof(ConsoleInfo->Colors[0]) ; ++i)
352 {
353 /*
354 * Write only the new value if we are saving the global settings
355 * or we are saving settings for a particular console, which differs
356 * from the default ones.
357 */
358 swprintf(szValueName, L"ColorTable%02d", i);
359 SetConsoleSetting(szValueName, REG_DWORD, sizeof(DWORD), &ConsoleInfo->Colors[i], s_Colors[i]);
360 }
361
362 SetConsoleSetting(L"HistoryBufferSize", REG_DWORD, sizeof(DWORD), &ConsoleInfo->HistoryBufferSize, 50);
363 SetConsoleSetting(L"NumberOfHistoryBuffers", REG_DWORD, sizeof(DWORD), &ConsoleInfo->NumberOfHistoryBuffers, 4);
364
365 Storage = ConsoleInfo->HistoryNoDup;
366 SetConsoleSetting(L"HistoryNoDup", REG_DWORD, sizeof(DWORD), &Storage, FALSE);
367
368 Storage = ConsoleInfo->QuickEdit;
369 SetConsoleSetting(L"QuickEdit", REG_DWORD, sizeof(DWORD), &Storage, FALSE);
370
371 Storage = ConsoleInfo->InsertMode;
372 SetConsoleSetting(L"InsertMode", REG_DWORD, sizeof(DWORD), &Storage, TRUE);
373
374 Storage = MAKELONG(ConsoleInfo->ScreenBufferSize.X, ConsoleInfo->ScreenBufferSize.Y);
375 SetConsoleSetting(L"ScreenBufferSize", REG_DWORD, sizeof(DWORD), &Storage, MAKELONG(80, 300));
376
377 Storage = MAKELONG(ConsoleInfo->ConsoleSize.X, ConsoleInfo->ConsoleSize.Y);
378 SetConsoleSetting(L"WindowSize", REG_DWORD, sizeof(DWORD), &Storage, MAKELONG(80, 25));
379
380 SetConsoleSetting(L"CursorSize", REG_DWORD, sizeof(DWORD), &ConsoleInfo->CursorSize, CSR_DEFAULT_CURSOR_SIZE);
381
382 Storage = ConsoleInfo->ScreenAttrib;
383 SetConsoleSetting(L"ScreenColors", REG_DWORD, sizeof(DWORD), &Storage, DEFAULT_SCREEN_ATTRIB);
384
385 Storage = ConsoleInfo->PopupAttrib;
386 SetConsoleSetting(L"PopupColors", REG_DWORD, sizeof(DWORD), &Storage, DEFAULT_POPUP_ATTRIB);
387
388 RegCloseKey(hKey);
389 return TRUE;
390 }
391
392 VOID
393 ConSrvGetDefaultSettings(IN OUT PCONSOLE_INFO ConsoleInfo,
394 IN DWORD ProcessId)
395 {
396 if (ConsoleInfo == NULL) return;
397
398 /// HKCU,"Console","LoadConIme",0x00010003,1
399
400 /*
401 * 1. Load the default values
402 */
403 // #define DEFAULT_HISTORY_COMMANDS_NUMBER 50
404 // #define DEFAULT_HISTORY_BUFFERS_NUMBER 4
405 ConsoleInfo->HistoryBufferSize = 50;
406 ConsoleInfo->NumberOfHistoryBuffers = 4;
407 ConsoleInfo->HistoryNoDup = FALSE;
408
409 ConsoleInfo->QuickEdit = FALSE;
410 ConsoleInfo->InsertMode = TRUE;
411 // ConsoleInfo->InputBufferSize;
412
413 // Rule: ScreenBufferSize >= ConsoleSize
414 ConsoleInfo->ScreenBufferSize.X = 80;
415 ConsoleInfo->ScreenBufferSize.Y = 300;
416 ConsoleInfo->ConsoleSize.X = 80;
417 ConsoleInfo->ConsoleSize.Y = 25;
418
419 ConsoleInfo->CursorBlinkOn;
420 ConsoleInfo->ForceCursorOff;
421 ConsoleInfo->CursorSize = CSR_DEFAULT_CURSOR_SIZE; // #define SMALL_SIZE 25
422
423 ConsoleInfo->ScreenAttrib = DEFAULT_SCREEN_ATTRIB;
424 ConsoleInfo->PopupAttrib = DEFAULT_POPUP_ATTRIB;
425
426 memcpy(ConsoleInfo->Colors, s_Colors, sizeof(s_Colors));
427
428 // ConsoleInfo->CodePage;
429
430 ConsoleInfo->ConsoleTitle[0] = L'\0';
431
432 /*
433 * 2. Overwrite them with the ones stored in HKCU\Console.
434 * If the HKCU\Console key doesn't exist, create it
435 * and store the default values inside.
436 */
437 if (!ConSrvReadUserSettings(ConsoleInfo, ProcessId))
438 {
439 ConSrvWriteUserSettings(ConsoleInfo, ProcessId);
440 }
441 }
442
443 VOID
444 ConSrvApplyUserSettings(IN PCONSOLE Console,
445 IN PCONSOLE_INFO ConsoleInfo)
446 {
447 PCONSOLE_SCREEN_BUFFER ActiveBuffer = Console->ActiveBuffer;
448
449 /*
450 * Apply terminal-edition settings:
451 * - QuickEdit and Insert modes,
452 * - history settings.
453 */
454 Console->QuickEdit = ConsoleInfo->QuickEdit;
455 Console->InsertMode = ConsoleInfo->InsertMode;
456
457 /*
458 * Apply foreground and background colors for both screen and popup
459 * and copy the new palette.
460 */
461 if (GetType(ActiveBuffer) == TEXTMODE_BUFFER)
462 {
463 PTEXTMODE_SCREEN_BUFFER Buffer = (PTEXTMODE_SCREEN_BUFFER)ActiveBuffer;
464
465 Buffer->ScreenDefaultAttrib = ConsoleInfo->ScreenAttrib;
466 Buffer->PopupDefaultAttrib = ConsoleInfo->PopupAttrib;
467 }
468 else // if (Console->ActiveBuffer->Header.Type == GRAPHICS_BUFFER)
469 {
470 }
471
472 // FIXME: Possible buffer overflow if s_colors is bigger than pConInfo->Colors.
473 memcpy(Console->Colors, ConsoleInfo->Colors, sizeof(s_Colors));
474
475 // TODO: Really update the screen attributes as FillConsoleOutputAttribute does.
476
477 /* Apply cursor size */
478 ActiveBuffer->CursorInfo.bVisible = (ConsoleInfo->CursorSize != 0);
479 ActiveBuffer->CursorInfo.dwSize = min(max(ConsoleInfo->CursorSize, 0), 100);
480
481 if (GetType(ActiveBuffer) == TEXTMODE_BUFFER)
482 {
483 PTEXTMODE_SCREEN_BUFFER Buffer = (PTEXTMODE_SCREEN_BUFFER)ActiveBuffer;
484 COORD BufSize;
485
486 /* Resize its active screen-buffer */
487 BufSize = ConsoleInfo->ScreenBufferSize;
488
489 if (Console->FixedSize)
490 {
491 /*
492 * The console is in fixed-size mode, so we cannot resize anything
493 * at the moment. However, keep those settings somewhere so that
494 * we can try to set them up when we will be allowed to do so.
495 */
496 if (ConsoleInfo->ConsoleSize.X != Buffer->OldViewSize.X ||
497 ConsoleInfo->ConsoleSize.Y != Buffer->OldViewSize.Y)
498 {
499 Buffer->OldViewSize = ConsoleInfo->ConsoleSize;
500 }
501
502 /* Buffer size is not allowed to be smaller than the view size */
503 if (BufSize.X >= Buffer->OldViewSize.X && BufSize.Y >= Buffer->OldViewSize.Y)
504 {
505 if (BufSize.X != Buffer->OldScreenBufferSize.X ||
506 BufSize.Y != Buffer->OldScreenBufferSize.Y)
507 {
508 /*
509 * The console is in fixed-size mode, so we cannot resize anything
510 * at the moment. However, keep those settings somewhere so that
511 * we can try to set them up when we will be allowed to do so.
512 */
513 Buffer->OldScreenBufferSize = BufSize;
514 }
515 }
516 }
517 else
518 {
519 BOOL SizeChanged = FALSE;
520
521 /* Resize the console */
522 if (ConsoleInfo->ConsoleSize.X != Buffer->ViewSize.X ||
523 ConsoleInfo->ConsoleSize.Y != Buffer->ViewSize.Y)
524 {
525 Buffer->ViewSize = ConsoleInfo->ConsoleSize;
526 SizeChanged = TRUE;
527 }
528
529 /* Resize the screen-buffer */
530 if (BufSize.X != Buffer->ScreenBufferSize.X ||
531 BufSize.Y != Buffer->ScreenBufferSize.Y)
532 {
533 if (NT_SUCCESS(ConioResizeBuffer(Console, Buffer, BufSize)))
534 SizeChanged = TRUE;
535 }
536
537 if (SizeChanged) ConioResizeTerminal(Console);
538 }
539 }
540 else // if (GetType(ActiveBuffer) == GRAPHICS_BUFFER)
541 {
542 PGRAPHICS_SCREEN_BUFFER Buffer = (PGRAPHICS_SCREEN_BUFFER)ActiveBuffer;
543
544 /*
545 * In any case we do NOT modify the size of the graphics screen-buffer.
546 * We just allow resizing the view only if the new size is smaller
547 * than the older one.
548 */
549
550 if (Console->FixedSize)
551 {
552 /*
553 * The console is in fixed-size mode, so we cannot resize anything
554 * at the moment. However, keep those settings somewhere so that
555 * we can try to set them up when we will be allowed to do so.
556 */
557 if (ConsoleInfo->ConsoleSize.X <= Buffer->ViewSize.X ||
558 ConsoleInfo->ConsoleSize.Y <= Buffer->ViewSize.Y)
559 {
560 Buffer->OldViewSize = ConsoleInfo->ConsoleSize;
561 }
562 }
563 else
564 {
565 /* Resize the view if its size is bigger than the specified size */
566 if (ConsoleInfo->ConsoleSize.X <= Buffer->ViewSize.X ||
567 ConsoleInfo->ConsoleSize.Y <= Buffer->ViewSize.Y)
568 {
569 Buffer->ViewSize = ConsoleInfo->ConsoleSize;
570 // SizeChanged = TRUE;
571 }
572 }
573 }
574 }
575
576 /* EOF */