[CONSRV]
[reactos.git] / win32ss / user / consrv / frontends / gui / guisettings.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: win32ss/user/consrv/frontends/gui/guisettings.c
5 * PURPOSE: GUI Terminal Front-End 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/settings.h"
15 #include "guisettings.h"
16
17 #define NDEBUG
18 #include <debug.h>
19
20
21 VOID GuiConsoleMoveWindow(PGUI_CONSOLE_DATA GuiData);
22
23 /* FUNCTIONS ******************************************************************/
24
25 BOOL
26 GuiConsoleReadUserSettings(IN OUT PGUI_CONSOLE_INFO TermInfo,
27 IN LPCWSTR ConsoleTitle,
28 IN DWORD ProcessId)
29 {
30 /*****************************************************
31 * Adapted from ConSrvReadUserSettings in settings.c *
32 *****************************************************/
33
34 BOOL RetVal = FALSE;
35 HKEY hKey;
36 DWORD dwNumSubKeys = 0;
37 DWORD dwIndex;
38 DWORD dwType;
39 WCHAR szValueName[MAX_PATH];
40 DWORD dwValueName;
41 WCHAR szValue[LF_FACESIZE] = L"\0";
42 DWORD Value;
43 DWORD dwValue;
44
45 if (!ConSrvOpenUserSettings(ProcessId,
46 ConsoleTitle,
47 &hKey, KEY_READ,
48 FALSE))
49 {
50 DPRINT("ConSrvOpenUserSettings failed\n");
51 return FALSE;
52 }
53
54 if (RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL,
55 &dwNumSubKeys, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
56 {
57 DPRINT("GuiConsoleReadUserSettings: RegQueryInfoKey failed\n");
58 RegCloseKey(hKey);
59 return FALSE;
60 }
61
62 DPRINT("GuiConsoleReadUserSettings entered dwNumSubKeys %d\n", dwNumSubKeys);
63
64 for (dwIndex = 0; dwIndex < dwNumSubKeys; dwIndex++)
65 {
66 dwValue = sizeof(Value);
67 dwValueName = MAX_PATH; // sizeof(szValueName)/sizeof(szValueName[0])
68
69 if (RegEnumValueW(hKey, dwIndex, szValueName, &dwValueName, NULL, &dwType, (BYTE*)&Value, &dwValue) != ERROR_SUCCESS)
70 {
71 if (dwType == REG_SZ)
72 {
73 /*
74 * Retry in case of string value
75 */
76 dwValue = sizeof(szValue);
77 dwValueName = MAX_PATH; // sizeof(szValueName)/sizeof(szValueName[0])
78 if (RegEnumValueW(hKey, dwIndex, szValueName, &dwValueName, NULL, NULL, (BYTE*)szValue, &dwValue) != ERROR_SUCCESS)
79 break;
80 }
81 else
82 {
83 break;
84 }
85 }
86
87 if (!wcscmp(szValueName, L"FaceName"))
88 {
89 SIZE_T Length = min(wcslen(szValue) + 1, LF_FACESIZE); // wcsnlen
90 wcsncpy(TermInfo->FaceName, szValue, LF_FACESIZE);
91 TermInfo->FaceName[Length] = L'\0';
92 RetVal = TRUE;
93 }
94 else if (!wcscmp(szValueName, L"FontFamily"))
95 {
96 TermInfo->FontFamily = Value;
97 RetVal = TRUE;
98 }
99 else if (!wcscmp(szValueName, L"FontSize"))
100 {
101 TermInfo->FontSize = Value;
102 RetVal = TRUE;
103 }
104 else if (!wcscmp(szValueName, L"FontWeight"))
105 {
106 TermInfo->FontWeight = Value;
107 RetVal = TRUE;
108 }
109 else if (!wcscmp(szValueName, L"WindowPosition"))
110 {
111 TermInfo->AutoPosition = FALSE;
112 TermInfo->WindowOrigin.x = LOWORD(Value);
113 TermInfo->WindowOrigin.y = HIWORD(Value);
114 RetVal = TRUE;
115 }
116 }
117
118 RegCloseKey(hKey);
119 return RetVal;
120 }
121
122 BOOL
123 GuiConsoleWriteUserSettings(IN OUT PGUI_CONSOLE_INFO TermInfo,
124 IN LPCWSTR ConsoleTitle,
125 IN DWORD ProcessId)
126 {
127 /******************************************************
128 * Adapted from ConSrvWriteUserSettings in settings.c *
129 ******************************************************/
130
131 BOOL GlobalSettings = (ConsoleTitle[0] == L'\0');
132 HKEY hKey;
133 DWORD Storage = 0;
134
135 #define SetConsoleSetting(SettingName, SettingType, SettingSize, Setting, DefaultValue) \
136 do { \
137 if (GlobalSettings || (!GlobalSettings && (*(Setting) != (DefaultValue)))) \
138 { \
139 RegSetValueExW(hKey, (SettingName), 0, (SettingType), (PBYTE)(Setting), (SettingSize)); \
140 } \
141 else \
142 { \
143 RegDeleteValue(hKey, (SettingName)); \
144 } \
145 } while (0)
146
147 if (!ConSrvOpenUserSettings(ProcessId,
148 ConsoleTitle,
149 &hKey, KEY_WRITE,
150 TRUE))
151 {
152 return FALSE;
153 }
154
155 SetConsoleSetting(L"FaceName", REG_SZ, (wcslen(TermInfo->FaceName) + 1) * sizeof(WCHAR), TermInfo->FaceName, L'\0'); // wcsnlen
156 SetConsoleSetting(L"FontFamily", REG_DWORD, sizeof(DWORD), &TermInfo->FontFamily, FF_DONTCARE);
157 SetConsoleSetting(L"FontSize", REG_DWORD, sizeof(DWORD), &TermInfo->FontSize, 0);
158 SetConsoleSetting(L"FontWeight", REG_DWORD, sizeof(DWORD), &TermInfo->FontWeight, FW_DONTCARE);
159
160 if (TermInfo->AutoPosition == FALSE)
161 {
162 Storage = MAKELONG(TermInfo->WindowOrigin.x, TermInfo->WindowOrigin.y);
163 RegSetValueExW(hKey, L"WindowPosition", 0, REG_DWORD, (PBYTE)&Storage, sizeof(DWORD));
164 }
165 else
166 {
167 RegDeleteValue(hKey, L"WindowPosition");
168 }
169
170 RegCloseKey(hKey);
171 return TRUE;
172 }
173
174 VOID
175 GuiConsoleGetDefaultSettings(IN OUT PGUI_CONSOLE_INFO TermInfo,
176 IN DWORD ProcessId)
177 {
178 /*******************************************************
179 * Adapted from ConSrvGetDefaultSettings in settings.c *
180 *******************************************************/
181
182 if (TermInfo == NULL) return;
183
184 /*
185 * 1. Load the default values
186 */
187 // wcsncpy(TermInfo->FaceName, L"DejaVu Sans Mono", LF_FACESIZE);
188 // TermInfo->FontSize = MAKELONG(12, 8); // 0x0008000C; // font is 8x12
189 // TermInfo->FontSize = MAKELONG(16, 16); // font is 16x16
190 // TermInfo->FontWeight = FW_NORMAL;
191
192 wcsncpy(TermInfo->FaceName, L"Fixedsys", LF_FACESIZE); // HACK: !!
193 // TermInfo->FaceName[0] = L'\0';
194 TermInfo->FontFamily = FF_DONTCARE;
195 TermInfo->FontSize = 0;
196 TermInfo->FontWeight = FW_DONTCARE;
197 TermInfo->UseRasterFonts = TRUE;
198
199 TermInfo->ShowWindow = SW_SHOWNORMAL;
200 TermInfo->AutoPosition = TRUE;
201 TermInfo->WindowOrigin.x = 0;
202 TermInfo->WindowOrigin.y = 0;
203
204 /*
205 * 2. Overwrite them with the ones stored in HKCU\Console.
206 * If the HKCU\Console key doesn't exist, create it
207 * and store the default values inside.
208 */
209 if (!GuiConsoleReadUserSettings(TermInfo, L"", ProcessId))
210 {
211 GuiConsoleWriteUserSettings(TermInfo, L"", ProcessId);
212 }
213 }
214
215 VOID
216 GuiConsoleShowConsoleProperties(PGUI_CONSOLE_DATA GuiData,
217 BOOL Defaults)
218 {
219 NTSTATUS Status;
220 PCONSOLE Console = GuiData->Console;
221 PCONSOLE_PROCESS_DATA ProcessData;
222 HANDLE hSection = NULL, hClientSection = NULL;
223 LARGE_INTEGER SectionSize;
224 ULONG ViewSize = 0;
225 SIZE_T Length = 0;
226 PCONSOLE_PROPS pSharedInfo = NULL;
227 PGUI_CONSOLE_INFO GuiInfo = NULL;
228
229 DPRINT("GuiConsoleShowConsoleProperties entered\n");
230
231 /*
232 * Create a memory section to share with the applet, and map it.
233 */
234 /* Holds data for console.dll + console info + terminal-specific info */
235 SectionSize.QuadPart = sizeof(CONSOLE_PROPS) + sizeof(GUI_CONSOLE_INFO);
236 Status = NtCreateSection(&hSection,
237 SECTION_ALL_ACCESS,
238 NULL,
239 &SectionSize,
240 PAGE_READWRITE,
241 SEC_COMMIT,
242 NULL);
243 if (!NT_SUCCESS(Status))
244 {
245 DPRINT1("Error: Impossible to create a shared section ; Status = %lu\n", Status);
246 return;
247 }
248
249 Status = NtMapViewOfSection(hSection,
250 NtCurrentProcess(),
251 (PVOID*)&pSharedInfo,
252 0,
253 0,
254 NULL,
255 &ViewSize,
256 ViewUnmap,
257 0,
258 PAGE_READWRITE);
259 if (!NT_SUCCESS(Status))
260 {
261 DPRINT1("Error: Impossible to map the shared section ; Status = %lu\n", Status);
262 NtClose(hSection);
263 return;
264 }
265
266
267 /*
268 * Setup the shared console properties structure.
269 */
270
271 /* Header */
272 pSharedInfo->hConsoleWindow = GuiData->hWindow;
273 pSharedInfo->ShowDefaultParams = Defaults;
274
275 /*
276 * We fill-in the fields only if we display
277 * our properties, not the default ones.
278 */
279 if (!Defaults)
280 {
281 /* Console information */
282 pSharedInfo->ci.HistoryBufferSize = Console->HistoryBufferSize;
283 pSharedInfo->ci.NumberOfHistoryBuffers = Console->NumberOfHistoryBuffers;
284 pSharedInfo->ci.HistoryNoDup = Console->HistoryNoDup;
285 pSharedInfo->ci.FullScreen = !!(Console->ActiveBuffer->DisplayMode & CONSOLE_FULLSCREEN_MODE);
286 pSharedInfo->ci.QuickEdit = Console->QuickEdit;
287 pSharedInfo->ci.InsertMode = Console->InsertMode;
288 pSharedInfo->ci.InputBufferSize = 0;
289 pSharedInfo->ci.ScreenBufferSize = Console->ActiveBuffer->ScreenBufferSize;
290 pSharedInfo->ci.ConsoleSize = Console->ConsoleSize;
291 pSharedInfo->ci.CursorBlinkOn;
292 pSharedInfo->ci.ForceCursorOff;
293 pSharedInfo->ci.CursorSize = Console->ActiveBuffer->CursorInfo.dwSize;
294 pSharedInfo->ci.ScreenAttrib = Console->ActiveBuffer->ScreenDefaultAttrib;
295 pSharedInfo->ci.PopupAttrib = Console->ActiveBuffer->PopupDefaultAttrib;
296 pSharedInfo->ci.CodePage;
297
298 /* GUI Information */
299 pSharedInfo->TerminalInfo.Size = sizeof(GUI_CONSOLE_INFO);
300 GuiInfo = pSharedInfo->TerminalInfo.TermInfo = (PGUI_CONSOLE_INFO)(pSharedInfo + 1);
301 Length = min(wcslen(GuiData->GuiInfo.FaceName) + 1, LF_FACESIZE); // wcsnlen
302 wcsncpy(GuiInfo->FaceName, GuiData->GuiInfo.FaceName, LF_FACESIZE);
303 GuiInfo->FaceName[Length] = L'\0';
304 GuiInfo->FontFamily = GuiData->GuiInfo.FontFamily;
305 GuiInfo->FontSize = GuiData->GuiInfo.FontSize;
306 GuiInfo->FontWeight = GuiData->GuiInfo.FontWeight;
307 GuiInfo->UseRasterFonts = GuiData->GuiInfo.UseRasterFonts;
308 /// GuiInfo->WindowPosition = GuiData->GuiInfo.WindowPosition;
309 GuiInfo->AutoPosition = GuiData->GuiInfo.AutoPosition;
310 GuiInfo->WindowOrigin = GuiData->GuiInfo.WindowOrigin;
311 /* Offsetize */
312 pSharedInfo->TerminalInfo.TermInfo = (PVOID)((ULONG_PTR)GuiInfo - (ULONG_PTR)pSharedInfo);
313
314 /* Palette */
315 memcpy(pSharedInfo->ci.Colors, Console->Colors, sizeof(Console->Colors));
316
317 /* Title of the console, original one corresponding to the one set by the console leader */
318 Length = min(sizeof(pSharedInfo->ci.ConsoleTitle) / sizeof(pSharedInfo->ci.ConsoleTitle[0]) - 1,
319 Console->OriginalTitle.Length / sizeof(WCHAR));
320 wcsncpy(pSharedInfo->ci.ConsoleTitle, Console->OriginalTitle.Buffer, Length);
321 }
322 else
323 {
324 Length = 0;
325 }
326
327 /* Null-terminate the title */
328 pSharedInfo->ci.ConsoleTitle[Length] = L'\0';
329
330
331 /* Unmap the view */
332 NtUnmapViewOfSection(NtCurrentProcess(), pSharedInfo);
333
334 /* Get the console leader process, our client */
335 ProcessData = CONTAINING_RECORD(Console->ProcessList.Blink,
336 CONSOLE_PROCESS_DATA,
337 ConsoleLink);
338
339 /* Duplicate the section handle for the client */
340 Status = NtDuplicateObject(NtCurrentProcess(),
341 hSection,
342 ProcessData->Process->ProcessHandle,
343 &hClientSection,
344 0, 0, DUPLICATE_SAME_ACCESS);
345 if (!NT_SUCCESS(Status))
346 {
347 DPRINT1("Error: Impossible to duplicate section handle for client ; Status = %lu\n", Status);
348 goto Quit;
349 }
350
351 /* Start the properties dialog */
352 if (ProcessData->PropDispatcher)
353 {
354 _SEH2_TRY
355 {
356 HANDLE Thread = NULL;
357
358 _SEH2_TRY
359 {
360 Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0,
361 ProcessData->PropDispatcher,
362 (PVOID)hClientSection, 0, NULL);
363 if (NULL == Thread)
364 {
365 DPRINT1("Failed thread creation (Error: 0x%x)\n", GetLastError());
366 }
367 else
368 {
369 DPRINT("ProcessData->PropDispatcher remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n", ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process);
370 /// WaitForSingleObject(Thread, INFINITE);
371 }
372 }
373 _SEH2_FINALLY
374 {
375 CloseHandle(Thread);
376 }
377 _SEH2_END;
378 }
379 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
380 {
381 Status = _SEH2_GetExceptionCode();
382 DPRINT1("GuiConsoleShowConsoleProperties - Caught an exception, Status = %08X\n", Status);
383 }
384 _SEH2_END;
385 }
386
387 Quit:
388 /* We have finished, close the section handle */
389 NtClose(hSection);
390 return;
391 }
392
393 NTSTATUS
394 GuiApplyUserSettings(PGUI_CONSOLE_DATA GuiData,
395 HANDLE hClientSection,
396 BOOL SaveSettings)
397 {
398 NTSTATUS Status = STATUS_SUCCESS;
399 PCONSOLE Console = GuiData->Console;
400 PCONSOLE_PROCESS_DATA ProcessData;
401 HANDLE hSection = NULL;
402 ULONG ViewSize = 0;
403 PCONSOLE_PROPS pConInfo = NULL;
404 PCONSOLE_INFO ConInfo = NULL;
405 PTERMINAL_INFO TermInfo = NULL;
406 PGUI_CONSOLE_INFO GuiInfo = NULL;
407
408 /* Get the console leader process, our client */
409 ProcessData = CONTAINING_RECORD(Console->ProcessList.Blink,
410 CONSOLE_PROCESS_DATA,
411 ConsoleLink);
412
413 /* Duplicate the section handle for ourselves */
414 Status = NtDuplicateObject(ProcessData->Process->ProcessHandle,
415 hClientSection,
416 NtCurrentProcess(),
417 &hSection,
418 0, 0, DUPLICATE_SAME_ACCESS);
419 if (!NT_SUCCESS(Status))
420 {
421 DPRINT1("Error when mapping client handle, Status = %lu\n", Status);
422 return Status;
423 }
424
425 /* Get a view of the shared section */
426 Status = NtMapViewOfSection(hSection,
427 NtCurrentProcess(),
428 (PVOID*)&pConInfo,
429 0,
430 0,
431 NULL,
432 &ViewSize,
433 ViewUnmap,
434 0,
435 PAGE_READONLY);
436 if (!NT_SUCCESS(Status))
437 {
438 DPRINT1("Error when mapping view of file, Status = %lu\n", Status);
439 NtClose(hSection);
440 return Status;
441 }
442
443 _SEH2_TRY
444 {
445 /* Check that the section is well-sized */
446 if ( (ViewSize < sizeof(CONSOLE_PROPS)) ||
447 (pConInfo->TerminalInfo.Size != sizeof(GUI_CONSOLE_INFO)) ||
448 (ViewSize < sizeof(CONSOLE_PROPS) + pConInfo->TerminalInfo.Size) )
449 {
450 DPRINT1("Error: section bad-sized: sizeof(Section) < sizeof(CONSOLE_PROPS) + sizeof(Terminal_specific_info)\n");
451 Status = STATUS_INVALID_VIEW_SIZE;
452 _SEH2_YIELD(goto Quit);
453 }
454
455 // TODO: Check that GuiData->hWindow == pConInfo->hConsoleWindow
456
457 /* Retrieve terminal informations */
458 ConInfo = &pConInfo->ci;
459 TermInfo = &pConInfo->TerminalInfo;
460 GuiInfo = TermInfo->TermInfo = (PVOID)((ULONG_PTR)pConInfo + (ULONG_PTR)TermInfo->TermInfo);
461
462 /*
463 * If we don't set the default parameters,
464 * apply them, otherwise just save them.
465 */
466 if (pConInfo->ShowDefaultParams == FALSE)
467 {
468 /* Set the console informations */
469 ConSrvApplyUserSettings(Console, ConInfo);
470
471 /* Set the terminal informations */
472
473 // memcpy(&GuiData->GuiInfo, GuiInfo, sizeof(GUI_CONSOLE_INFO));
474
475 /* Move the window to the user's values */
476 GuiData->GuiInfo.AutoPosition = GuiInfo->AutoPosition;
477 GuiData->GuiInfo.WindowOrigin = GuiInfo->WindowOrigin;
478 GuiConsoleMoveWindow(GuiData);
479
480 InvalidateRect(GuiData->hWindow, NULL, TRUE);
481 }
482
483 /*
484 * Save settings if needed
485 */
486 // FIXME: Do it in the console properties applet ??
487 if (SaveSettings)
488 {
489 DWORD ProcessId = HandleToUlong(ProcessData->Process->ClientId.UniqueProcess);
490 ConSrvWriteUserSettings(ConInfo, ProcessId);
491 GuiConsoleWriteUserSettings(GuiInfo, ConInfo->ConsoleTitle, ProcessId);
492 }
493
494 Status = STATUS_SUCCESS;
495 }
496 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
497 {
498 Status = _SEH2_GetExceptionCode();
499 DPRINT1("GuiApplyUserSettings - Caught an exception, Status = %08X\n", Status);
500 }
501 _SEH2_END;
502
503 Quit:
504 /* Finally, close the section and return */
505 NtUnmapViewOfSection(NtCurrentProcess(), pConInfo);
506 NtClose(hSection);
507 return Status;
508 }
509
510 /* EOF */