1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Console Server DLL
4  * FILE:            win32ss/user/winsrv/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 
14 #define NDEBUG
15 #include <debug.h>
16 
17 #include "concfg/font.h"
18 #include "guiterm.h"
19 #include "guisettings.h"
20 
21 /* FUNCTIONS ******************************************************************/
22 
23 BOOL
24 GuiConsoleReadUserSettings(IN OUT PGUI_CONSOLE_INFO TermInfo)
25 {
26     /* Do nothing */
27     return TRUE;
28 }
29 
30 BOOL
31 GuiConsoleWriteUserSettings(IN OUT PGUI_CONSOLE_INFO TermInfo)
32 {
33     /* Do nothing */
34     return TRUE;
35 }
36 
37 VOID
38 GuiConsoleGetDefaultSettings(IN OUT PGUI_CONSOLE_INFO TermInfo)
39 {
40     /* Do nothing */
41 }
42 
43 VOID
44 GuiConsoleShowConsoleProperties(PGUI_CONSOLE_DATA GuiData,
45                                 BOOL Defaults)
46 {
47     NTSTATUS Status;
48     PCONSRV_CONSOLE Console = GuiData->Console;
49     PCONSOLE_PROCESS_DATA ProcessData;
50     HANDLE hSection = NULL, hClientSection = NULL;
51     PVOID ThreadParameter = NULL; // Is either hClientSection or the console window handle,
52                                   // depending on whether we display the default settings or
53                                   // the settings of a particular console.
54 
55     DPRINT("GuiConsoleShowConsoleProperties entered\n");
56 
57     if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE)) return;
58 
59     /* Get the console leader process, our client */
60     ProcessData = ConSrvGetConsoleLeaderProcess(Console);
61 
62     /*
63      * Be sure we effectively have a properties dialog routine (that launches
64      * the console control panel applet). It resides in kernel32.dll (client).
65      */
66     if (ProcessData->PropRoutine == NULL) goto Quit;
67 
68     /*
69      * Create a memory section to be shared with the console control panel applet
70      * in the case we are displaying the settings of a particular console.
71      * In that case the ThreadParameter is the hClientSection handle.
72      * In the case we display the default console parameters, we don't need to
73      * create a memory section. We just need to open the applet, and in this case
74      * the ThreadParameter is the parent window handle of the applet's window,
75      * that is, the console window.
76      */
77     if (!Defaults)
78     {
79         PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer;
80         LARGE_INTEGER SectionSize;
81         SIZE_T ViewSize = 0;
82         PCONSOLE_STATE_INFO pSharedInfo = NULL;
83 
84         /*
85          * Create a memory section to share with the applet, and map it.
86          */
87         SectionSize.QuadPart  = sizeof(CONSOLE_STATE_INFO);    // Standard size
88         SectionSize.QuadPart += Console->OriginalTitle.Length; // Add the length in bytes of the console title string
89 
90         Status = NtCreateSection(&hSection,
91                                  SECTION_ALL_ACCESS,
92                                  NULL,
93                                  &SectionSize,
94                                  PAGE_READWRITE,
95                                  SEC_COMMIT,
96                                  NULL);
97         if (!NT_SUCCESS(Status))
98         {
99             DPRINT1("Error: Impossible to create a shared section, Status = 0x%08lx\n", Status);
100             goto Quit;
101         }
102 
103         Status = NtMapViewOfSection(hSection,
104                                     NtCurrentProcess(),
105                                     (PVOID*)&pSharedInfo,
106                                     0,
107                                     0,
108                                     NULL,
109                                     &ViewSize,
110                                     ViewUnmap,
111                                     0,
112                                     PAGE_READWRITE);
113         if (!NT_SUCCESS(Status))
114         {
115             DPRINT1("Error: Impossible to map the shared section, Status = 0x%08lx\n", Status);
116             goto Quit;
117         }
118 
119 
120         /*
121          * Setup the shared console properties structure.
122          */
123 
124         /* Store the real size of the structure */
125         pSharedInfo->cbSize = SectionSize.QuadPart;
126 
127         /*
128          * When we setup the settings of a particular console, the parent window
129          * of the applet's window is the console window, and it is given via the
130          * hWnd member of the shared console info structure.
131          */
132         pSharedInfo->hWnd = GuiData->hWindow;
133 
134         /* Console information */
135         pSharedInfo->QuickEdit = Console->QuickEdit;
136         pSharedInfo->InsertMode = Console->InsertMode;
137         pSharedInfo->NumberOfHistoryBuffers = Console->MaxNumberOfHistoryBuffers;
138         pSharedInfo->HistoryBufferSize = Console->HistoryBufferSize;
139         pSharedInfo->HistoryNoDup = Console->HistoryNoDup;
140         /// pSharedInfo->InputBufferSize = 0;
141         pSharedInfo->ScreenBufferSize = ActiveBuffer->ScreenBufferSize;
142         pSharedInfo->WindowSize = ActiveBuffer->ViewSize;
143         pSharedInfo->CursorSize = ActiveBuffer->CursorInfo.dwSize;
144         if (GetType(ActiveBuffer) == TEXTMODE_BUFFER)
145         {
146             PTEXTMODE_SCREEN_BUFFER Buffer = (PTEXTMODE_SCREEN_BUFFER)ActiveBuffer;
147 
148             pSharedInfo->ScreenAttributes = Buffer->ScreenDefaultAttrib;
149             pSharedInfo->PopupAttributes  = Buffer->PopupDefaultAttrib;
150         }
151         else // if (GetType(ActiveBuffer) == GRAPHICS_BUFFER)
152         {
153             // PGRAPHICS_SCREEN_BUFFER Buffer = (PGRAPHICS_SCREEN_BUFFER)ActiveBuffer;
154 
155             // FIXME: Gather defaults from the registry ?
156             pSharedInfo->ScreenAttributes = DEFAULT_SCREEN_ATTRIB;
157             pSharedInfo->PopupAttributes  = DEFAULT_POPUP_ATTRIB;
158         }
159 
160         /* We display the output code page only */
161         pSharedInfo->CodePage = Console->OutputCodePage;
162 
163         /* GUI Information */
164         StringCchCopyNW(pSharedInfo->FaceName, ARRAYSIZE(pSharedInfo->FaceName),
165                         GuiData->GuiInfo.FaceName, ARRAYSIZE(GuiData->GuiInfo.FaceName));
166         pSharedInfo->FontFamily = GuiData->GuiInfo.FontFamily;
167         pSharedInfo->FontSize   = GuiData->GuiInfo.FontSize;
168         pSharedInfo->FontWeight = GuiData->GuiInfo.FontWeight;
169         pSharedInfo->FullScreen = GuiData->GuiInfo.FullScreen;
170         pSharedInfo->AutoPosition   = GuiData->GuiInfo.AutoPosition;
171         pSharedInfo->WindowPosition = GuiData->GuiInfo.WindowOrigin;
172 
173         /* Palette */
174         RtlCopyMemory(pSharedInfo->ColorTable,
175                       Console->Colors, sizeof(Console->Colors));
176 
177         /* Copy the original title of the console and null-terminate it */
178         RtlCopyMemory(pSharedInfo->ConsoleTitle,
179                       Console->OriginalTitle.Buffer,
180                       Console->OriginalTitle.Length);
181 
182         pSharedInfo->ConsoleTitle[Console->OriginalTitle.Length / sizeof(WCHAR)] = UNICODE_NULL;
183 
184 
185         /* Unmap the view */
186         NtUnmapViewOfSection(NtCurrentProcess(), pSharedInfo);
187 
188         /* Duplicate the section handle for the client */
189         Status = NtDuplicateObject(NtCurrentProcess(),
190                                    hSection,
191                                    ProcessData->Process->ProcessHandle,
192                                    &hClientSection,
193                                    0, 0, DUPLICATE_SAME_ACCESS);
194         if (!NT_SUCCESS(Status))
195         {
196             DPRINT1("Error: Impossible to duplicate section handle for client, Status = 0x%08lx\n", Status);
197             goto Quit;
198         }
199 
200         /* For the settings of a particular console, use the shared client section handle as the thread parameter */
201         ThreadParameter = (PVOID)hClientSection;
202     }
203     else
204     {
205         /* For the default settings, use the console window handle as the thread parameter */
206         ThreadParameter = (PVOID)GuiData->hWindow;
207     }
208 
209     /* Start the console control panel applet */
210     _SEH2_TRY
211     {
212         HANDLE Thread = NULL;
213 
214         _SEH2_TRY
215         {
216             Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0,
217                                         ProcessData->PropRoutine,
218                                         ThreadParameter, 0, NULL);
219             if (NULL == Thread)
220             {
221                 DPRINT1("Failed thread creation (Error: 0x%x)\n", GetLastError());
222             }
223             else
224             {
225                 DPRINT("ProcessData->PropRoutine remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n",
226                        ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process);
227             }
228         }
229         _SEH2_FINALLY
230         {
231             CloseHandle(Thread);
232         }
233         _SEH2_END;
234     }
235     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
236     {
237         Status = _SEH2_GetExceptionCode();
238         DPRINT1("GuiConsoleShowConsoleProperties - Caught an exception, Status = 0x%08lx\n", Status);
239     }
240     _SEH2_END;
241 
242 Quit:
243     /* We have finished, close the section handle if any */
244     if (hSection) NtClose(hSection);
245 
246     LeaveCriticalSection(&Console->Lock);
247     return;
248 }
249 
250 /*
251  * Function for dealing with the undocumented message and structure used by
252  * Windows' console.dll for setting console info.
253  * See http://www.catch22.net/sites/default/source/files/setconsoleinfo.c
254  * and http://www.scn.rain.com/~neighorn/PDF/MSBugPaper.pdf
255  * for more information.
256  */
257 VOID
258 GuiApplyUserSettings(PGUI_CONSOLE_DATA GuiData,
259                      HANDLE hClientSection)
260 {
261     NTSTATUS Status = STATUS_SUCCESS;
262     PCONSRV_CONSOLE Console = GuiData->Console;
263     PCONSOLE_PROCESS_DATA ProcessData;
264     HANDLE hSection = NULL;
265     SIZE_T ViewSize = 0;
266     PCONSOLE_STATE_INFO pConInfo = NULL;
267 
268     if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE)) return;
269 
270     /* Get the console leader process, our client */
271     ProcessData = ConSrvGetConsoleLeaderProcess(Console);
272 
273     /* Duplicate the section handle for ourselves */
274     Status = NtDuplicateObject(ProcessData->Process->ProcessHandle,
275                                hClientSection,
276                                NtCurrentProcess(),
277                                &hSection,
278                                0, 0, DUPLICATE_SAME_ACCESS);
279     if (!NT_SUCCESS(Status))
280     {
281         DPRINT1("Error when mapping client handle, Status = 0x%08lx\n", Status);
282         goto Quit;
283     }
284 
285     /* Get a view of the shared section */
286     Status = NtMapViewOfSection(hSection,
287                                 NtCurrentProcess(),
288                                 (PVOID*)&pConInfo,
289                                 0,
290                                 0,
291                                 NULL,
292                                 &ViewSize,
293                                 ViewUnmap,
294                                 0,
295                                 PAGE_READWRITE);
296     if (!NT_SUCCESS(Status))
297     {
298         DPRINT1("Error when mapping view of file, Status = 0x%08lx\n", Status);
299         goto Quit;
300     }
301 
302     _SEH2_TRY
303     {
304         /* Check that the section is well-sized */
305         if ( (ViewSize < sizeof(CONSOLE_STATE_INFO)) ||
306              (pConInfo->cbSize < sizeof(CONSOLE_STATE_INFO)) )
307         {
308             DPRINT1("Error: section bad-sized: sizeof(Section) < sizeof(CONSOLE_STATE_INFO)\n");
309             Status = STATUS_INVALID_VIEW_SIZE;
310             _SEH2_YIELD(goto Quit);
311         }
312 
313         // TODO: Check that GuiData->hWindow == pConInfo->hWnd
314 
315         /* Retrieve terminal informations */
316 
317         /* Console information */
318 
319         /*
320          * Apply the settings
321          */
322 
323         /* Set the console informations */
324         ConSrvApplyUserSettings(Console, pConInfo);
325 
326         /* Set the terminal informations */
327 
328         /* Refresh the additional TrueType fonts cache and change the font */
329         RefreshTTFontCache();
330         InitFonts(GuiData,
331                   pConInfo->FaceName,
332                   pConInfo->FontFamily,
333                   pConInfo->FontSize,
334                   pConInfo->FontWeight);
335        // HACK, needed because changing font may change the size of the window
336        /**/TermResizeTerminal(Console);/**/
337 
338         /* Move the window to the user's values */
339         GuiData->GuiInfo.AutoPosition = !!pConInfo->AutoPosition;
340         GuiData->GuiInfo.WindowOrigin = pConInfo->WindowPosition;
341         GuiConsoleMoveWindow(GuiData);
342 
343         InvalidateRect(GuiData->hWindow, NULL, TRUE);
344 
345         /*
346          * Apply full-screen mode.
347          */
348         if (!!pConInfo->FullScreen != GuiData->GuiInfo.FullScreen)
349         {
350             SwitchFullScreen(GuiData, !!pConInfo->FullScreen);
351         }
352 
353         /*
354          * The settings are saved in the registry by console.dll itself, if needed.
355          */
356         // if (SaveSettings)
357         // {
358             // GuiConsoleWriteUserSettings(GuiInfo);
359         // }
360 
361         Status = STATUS_SUCCESS;
362     }
363     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
364     {
365         Status = _SEH2_GetExceptionCode();
366         DPRINT1("GuiApplyUserSettings - Caught an exception, Status = 0x%08lx\n", Status);
367     }
368     _SEH2_END;
369 
370 Quit:
371     /* Finally, close the section and return */
372     if (hSection)
373     {
374         NtUnmapViewOfSection(NtCurrentProcess(), pConInfo);
375         NtClose(hSection);
376     }
377 
378     LeaveCriticalSection(&Console->Lock);
379     return;
380 }
381 
382 /* EOF */
383