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 "guiterm.h"
18 #include "guisettings.h"
19 
20 /* FUNCTIONS ******************************************************************/
21 
22 BOOL
23 GuiConsoleReadUserSettings(IN OUT PGUI_CONSOLE_INFO TermInfo)
24 {
25     /* Do nothing */
26     return TRUE;
27 }
28 
29 BOOL
30 GuiConsoleWriteUserSettings(IN OUT PGUI_CONSOLE_INFO TermInfo)
31 {
32     /* Do nothing */
33     return TRUE;
34 }
35 
36 VOID
37 GuiConsoleGetDefaultSettings(IN OUT PGUI_CONSOLE_INFO TermInfo)
38 {
39     /* Do nothing */
40 }
41 
42 VOID
43 GuiConsoleShowConsoleProperties(PGUI_CONSOLE_DATA GuiData,
44                                 BOOL Defaults)
45 {
46     NTSTATUS Status;
47     PCONSRV_CONSOLE Console = GuiData->Console;
48     PCONSOLE_PROCESS_DATA ProcessData;
49     HANDLE hSection = NULL, hClientSection = NULL;
50     PVOID ThreadParameter = NULL; // Is either hClientSection or the console window handle,
51                                   // depending on whether we display the default settings or
52                                   // the settings of a particular console.
53 
54     DPRINT("GuiConsoleShowConsoleProperties entered\n");
55 
56     if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE)) return;
57 
58     /* Get the console leader process, our client */
59     ProcessData = ConSrvGetConsoleLeaderProcess(Console);
60 
61     /*
62      * Be sure we effectively have a properties dialog routine (that launches
63      * the console control panel applet). It resides in kernel32.dll (client).
64      */
65     if (ProcessData->PropRoutine == NULL) goto Quit;
66 
67     /*
68      * Create a memory section to be shared with the console control panel applet
69      * in the case we are displaying the settings of a particular console.
70      * In that case the ThreadParameter is the hClientSection handle.
71      * In the case we display the default console parameters, we don't need to
72      * create a memory section. We just need to open the applet, and in this case
73      * the ThreadParameter is the parent window handle of the applet's window,
74      * that is, the console window.
75      */
76     if (!Defaults)
77     {
78         PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer;
79         LARGE_INTEGER SectionSize;
80         SIZE_T ViewSize = 0;
81         PCONSOLE_STATE_INFO pSharedInfo = NULL;
82 
83         /*
84          * Create a memory section to share with the applet, and map it.
85          */
86         SectionSize.QuadPart  = sizeof(CONSOLE_STATE_INFO);    // Standard size
87         SectionSize.QuadPart += Console->OriginalTitle.Length; // Add the length in bytes of the console title string
88 
89         Status = NtCreateSection(&hSection,
90                                  SECTION_ALL_ACCESS,
91                                  NULL,
92                                  &SectionSize,
93                                  PAGE_READWRITE,
94                                  SEC_COMMIT,
95                                  NULL);
96         if (!NT_SUCCESS(Status))
97         {
98             DPRINT1("Error: Impossible to create a shared section, Status = 0x%08lx\n", Status);
99             goto Quit;
100         }
101 
102         Status = NtMapViewOfSection(hSection,
103                                     NtCurrentProcess(),
104                                     (PVOID*)&pSharedInfo,
105                                     0,
106                                     0,
107                                     NULL,
108                                     &ViewSize,
109                                     ViewUnmap,
110                                     0,
111                                     PAGE_READWRITE);
112         if (!NT_SUCCESS(Status))
113         {
114             DPRINT1("Error: Impossible to map the shared section, Status = 0x%08lx\n", Status);
115             goto Quit;
116         }
117 
118 
119         /*
120          * Setup the shared console properties structure.
121          */
122 
123         /* Store the real size of the structure */
124         pSharedInfo->cbSize = SectionSize.QuadPart;
125 
126         /*
127          * When we setup the settings of a particular console, the parent window
128          * of the applet's window is the console window, and it is given via the
129          * hWnd member of the shared console info structure.
130          */
131         pSharedInfo->hWnd = GuiData->hWindow;
132 
133         /* Console information */
134         pSharedInfo->HistoryBufferSize = Console->HistoryBufferSize;
135         pSharedInfo->NumberOfHistoryBuffers = Console->NumberOfHistoryBuffers;
136         pSharedInfo->HistoryNoDup = Console->HistoryNoDup;
137         pSharedInfo->QuickEdit = Console->QuickEdit;
138         pSharedInfo->InsertMode = Console->InsertMode;
139         /// pSharedInfo->InputBufferSize = 0;
140         pSharedInfo->ScreenBufferSize = ActiveBuffer->ScreenBufferSize;
141         pSharedInfo->WindowSize = ActiveBuffer->ViewSize;
142         pSharedInfo->CursorSize = ActiveBuffer->CursorInfo.dwSize;
143         if (GetType(ActiveBuffer) == TEXTMODE_BUFFER)
144         {
145             PTEXTMODE_SCREEN_BUFFER Buffer = (PTEXTMODE_SCREEN_BUFFER)ActiveBuffer;
146 
147             pSharedInfo->ScreenAttributes = Buffer->ScreenDefaultAttrib;
148             pSharedInfo->PopupAttributes  = Buffer->PopupDefaultAttrib;
149         }
150         else // if (GetType(ActiveBuffer) == GRAPHICS_BUFFER)
151         {
152             // PGRAPHICS_SCREEN_BUFFER Buffer = (PGRAPHICS_SCREEN_BUFFER)ActiveBuffer;
153             DPRINT1("GuiConsoleShowConsoleProperties - Graphics buffer\n");
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 #if 0 // FIXME: Things not set
319         ConInfo.HistoryBufferSize = pConInfo->HistoryBufferSize;
320         ConInfo.NumberOfHistoryBuffers = pConInfo->NumberOfHistoryBuffers;
321         ConInfo.HistoryNoDup = !!pConInfo->HistoryNoDup;
322         ConInfo.CodePage = pConInfo->CodePage; // Done in ConSrvApplyUserSettings
323 #endif
324 
325         /*
326          * Apply the settings
327          */
328 
329         /* Set the console informations */
330         ConSrvApplyUserSettings(Console, pConInfo);
331 
332         /* Set the terminal informations */
333 
334         /* Change the font */
335         InitFonts(GuiData,
336                   pConInfo->FaceName,
337                   pConInfo->FontFamily,
338                   pConInfo->FontSize,
339                   pConInfo->FontWeight);
340        // HACK, needed because changing font may change the size of the window
341        /**/TermResizeTerminal(Console);/**/
342 
343         /* Move the window to the user's values */
344         GuiData->GuiInfo.AutoPosition = !!pConInfo->AutoPosition;
345         GuiData->GuiInfo.WindowOrigin = pConInfo->WindowPosition;
346         GuiConsoleMoveWindow(GuiData);
347 
348         InvalidateRect(GuiData->hWindow, NULL, TRUE);
349 
350         /*
351          * Apply full-screen mode.
352          */
353         if (!!pConInfo->FullScreen != GuiData->GuiInfo.FullScreen)
354         {
355             SwitchFullScreen(GuiData, !!pConInfo->FullScreen);
356         }
357 
358         /*
359          * The settings are saved in the registry by console.dll itself, if needed.
360          */
361         // if (SaveSettings)
362         // {
363             // GuiConsoleWriteUserSettings(GuiInfo);
364         // }
365 
366         Status = STATUS_SUCCESS;
367     }
368     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
369     {
370         Status = _SEH2_GetExceptionCode();
371         DPRINT1("GuiApplyUserSettings - Caught an exception, Status = 0x%08lx\n", Status);
372     }
373     _SEH2_END;
374 
375 Quit:
376     /* Finally, close the section and return */
377     if (hSection)
378     {
379         NtUnmapViewOfSection(NtCurrentProcess(), pConInfo);
380         NtClose(hSection);
381     }
382 
383     LeaveCriticalSection(&Console->Lock);
384     return;
385 }
386 
387 /* EOF */
388