1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Console Driver DLL
4  * FILE:            win32ss/user/winsrv/consrv/condrv/console.c
5  * PURPOSE:         Console Management Functions
6  * PROGRAMMERS:     G� van Geldorp
7  *                  Jeffrey Morlan
8  *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
9  *                  Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
10  */
11 
12 /* INCLUDES *******************************************************************/
13 
14 #include <consrv.h>
15 #include <coninput.h>
16 #include "../../concfg/font.h"
17 
18 #define NDEBUG
19 #include <debug.h>
20 
21 /* CONSOLE VALIDATION FUNCTIONS ***********************************************/
22 
23 BOOLEAN NTAPI
24 ConDrvValidateConsoleState(IN PCONSOLE Console,
25                            IN CONSOLE_STATE ExpectedState)
26 {
27     // if (!Console) return FALSE;
28 
29     /* The console must be locked */
30     // ASSERT(Console_locked);
31 
32     return (Console->State == ExpectedState);
33 }
34 
35 BOOLEAN NTAPI
36 ConDrvValidateConsoleUnsafe(IN PCONSOLE Console,
37                             IN CONSOLE_STATE ExpectedState,
38                             IN BOOLEAN LockConsole)
39 {
40     if (!Console) return FALSE;
41 
42     /*
43      * Lock the console to forbid possible console's state changes
44      * (which must be done when the console is already locked).
45      * If we don't want to lock it, it's because the lock is already
46      * held. So there must be no problems.
47      */
48     if (LockConsole) EnterCriticalSection(&Console->Lock);
49 
50     // ASSERT(Console_locked);
51 
52     /* Check whether the console's state is what we expect */
53     if (!ConDrvValidateConsoleState(Console, ExpectedState))
54     {
55         if (LockConsole) LeaveCriticalSection(&Console->Lock);
56         return FALSE;
57     }
58 
59     return TRUE;
60 }
61 
62 
63 /* CONSOLE INITIALIZATION FUNCTIONS *******************************************/
64 
65 /* For resetting the terminal - defined in dummyterm.c */
66 VOID ResetTerminal(IN PCONSOLE Console);
67 
68 NTSTATUS NTAPI
69 ConDrvInitConsole(
70     IN OUT PCONSOLE Console,
71     IN PCONSOLE_INFO ConsoleInfo)
72 {
73     NTSTATUS Status;
74     // CONSOLE_INFO CapturedConsoleInfo;
75     TEXTMODE_BUFFER_INFO ScreenBufferInfo;
76     PCONSOLE_SCREEN_BUFFER NewBuffer;
77 
78     if (Console == NULL || ConsoleInfo == NULL)
79         return STATUS_INVALID_PARAMETER;
80 
81     /* Reset the console structure */
82     RtlZeroMemory(Console, sizeof(*Console));
83 
84     /*
85      * Set and fix the screen buffer size if needed.
86      * The rule is: ScreenBufferSize >= ConsoleSize
87      */
88     if (ConsoleInfo->ScreenBufferSize.X == 0) ConsoleInfo->ScreenBufferSize.X = 1;
89     if (ConsoleInfo->ScreenBufferSize.Y == 0) ConsoleInfo->ScreenBufferSize.Y = 1;
90     if (ConsoleInfo->ScreenBufferSize.X < ConsoleInfo->ConsoleSize.X)
91         ConsoleInfo->ScreenBufferSize.X = ConsoleInfo->ConsoleSize.X;
92     if (ConsoleInfo->ScreenBufferSize.Y < ConsoleInfo->ConsoleSize.Y)
93         ConsoleInfo->ScreenBufferSize.Y = ConsoleInfo->ConsoleSize.Y;
94 
95     /*
96      * Initialize the console
97      */
98     Console->State = CONSOLE_INITIALIZING;
99     Console->ReferenceCount = 0;
100     InitializeCriticalSection(&Console->Lock);
101 
102     /* Initialize the terminal interface */
103     ResetTerminal(Console);
104 
105     Console->ConsoleSize = ConsoleInfo->ConsoleSize;
106     Console->FixedSize   = FALSE; // Value by default; is reseted by the terminals if needed.
107 
108     /* Initialize the input buffer */
109     Status = ConDrvInitInputBuffer(Console, 0 /* ConsoleInfo->InputBufferSize */);
110     if (!NT_SUCCESS(Status))
111     {
112         DPRINT1("ConDrvInitInputBuffer: failed, Status = 0x%08lx\n", Status);
113         DeleteCriticalSection(&Console->Lock);
114         return Status;
115     }
116 
117     /* Set-up the code page */
118     if (IsValidCodePage(ConsoleInfo->CodePage))
119         Console->InputCodePage = Console->OutputCodePage = ConsoleInfo->CodePage;
120 
121     Console->IsCJK = IsCJKCodePage(Console->OutputCodePage);
122 
123     /* Initialize a new text-mode screen buffer with default settings */
124     ScreenBufferInfo.ScreenBufferSize = ConsoleInfo->ScreenBufferSize;
125     ScreenBufferInfo.ViewSize         = ConsoleInfo->ConsoleSize;
126     ScreenBufferInfo.ScreenAttrib     = ConsoleInfo->ScreenAttrib;
127     ScreenBufferInfo.PopupAttrib      = ConsoleInfo->PopupAttrib;
128     ScreenBufferInfo.CursorSize       = ConsoleInfo->CursorSize;
129     ScreenBufferInfo.IsCursorVisible  = TRUE;
130 
131     InitializeListHead(&Console->BufferList);
132     Status = ConDrvCreateScreenBuffer(&NewBuffer,
133                                       Console,
134                                       NULL,
135                                       CONSOLE_TEXTMODE_BUFFER,
136                                       &ScreenBufferInfo);
137     if (!NT_SUCCESS(Status))
138     {
139         DPRINT1("ConDrvCreateScreenBuffer: failed, Status = 0x%08lx\n", Status);
140         ConDrvDeinitInputBuffer(Console);
141         DeleteCriticalSection(&Console->Lock);
142         return Status;
143     }
144     /* Make the new screen buffer active */
145     Console->ActiveBuffer = NewBuffer;
146     Console->ConsolePaused = FALSE;
147 
148     DPRINT("Console initialized\n");
149 
150     /* The initialization is finished */
151     DPRINT("Change state\n");
152     Console->State = CONSOLE_RUNNING;
153 
154     /* The caller now has a newly initialized console */
155     return STATUS_SUCCESS;
156 }
157 
158 NTSTATUS NTAPI
159 ConDrvAttachTerminal(IN PCONSOLE Console,
160                      IN PTERMINAL Terminal)
161 {
162     NTSTATUS Status;
163 
164     if (Console == NULL || Terminal == NULL)
165         return STATUS_INVALID_PARAMETER;
166 
167     /* FIXME: Lock the console before ?? */
168 
169     /*
170      * Attach the terminal to the console. Use now the TermIFace of the console,
171      * and not the user-defined temporary Terminal pointer.
172      */
173     Console->TermIFace = *Terminal;
174     Console->TermIFace.Console = Console;
175 
176     /* Initialize the terminal AFTER having attached it to the console */
177     DPRINT("Finish initialization of terminal\n");
178     Status = Console->TermIFace.Vtbl->InitTerminal(&Console->TermIFace, Console);
179     if (!NT_SUCCESS(Status))
180     {
181         DPRINT1("Terminal initialization failed, Status = 0x%08lx\n", Status);
182 
183         /* We failed, detach the terminal from the console */
184         Terminal->Console = NULL; // For the caller
185         ResetTerminal(Console);
186         return Status;
187     }
188 
189     /* Copy buffer contents to screen */
190     // Terminal.Draw();
191 
192     DPRINT("Terminal initialization done\n");
193     return STATUS_SUCCESS;
194 }
195 
196 NTSTATUS NTAPI
197 ConDrvDetachTerminal(IN PCONSOLE Console)
198 {
199     if (Console == NULL) return STATUS_INVALID_PARAMETER;
200 
201     /* FIXME: Lock the console before ?? */
202 
203     /* Deinitialize the terminal BEFORE detaching it from the console */
204     Console->TermIFace.Vtbl->DeinitTerminal(&Console->TermIFace/*, Console*/);
205 
206     /*
207      * Detach the terminal from the console:
208      * reinitialize the terminal interface.
209      */
210     ResetTerminal(Console);
211 
212     DPRINT("Terminal unregistered\n");
213     return STATUS_SUCCESS;
214 }
215 
216 VOID NTAPI
217 ConDrvDeleteConsole(IN PCONSOLE Console)
218 {
219     DPRINT("ConDrvDeleteConsole(0x%p)\n", Console);
220 
221     /*
222      * Forbid validation of any console by other threads
223      * during the deletion of this console.
224      */
225     // ConDrvLockConsoleListExclusive();
226 
227     /*
228      * If the console is already being destroyed, i.e. not running
229      * or finishing to be initialized, just return.
230      */
231     if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE) &&
232         !ConDrvValidateConsoleUnsafe(Console, CONSOLE_INITIALIZING, TRUE))
233     {
234         return;
235     }
236 
237     /*
238      * We are about to be destroyed. Signal it to other people
239      * so that they can terminate what they are doing, and that
240      * they cannot longer validate the console.
241      */
242     Console->State = CONSOLE_TERMINATING;
243 
244     /*
245      * Allow other threads to finish their job: basically, unlock
246      * all other calls to EnterCriticalSection(&Console->Lock); by
247      * ConDrvValidateConsoleUnsafe() functions so that they just see
248      * that we are not in CONSOLE_RUNNING state anymore, or unlock
249      * other concurrent calls to ConDrvDeleteConsole() so that they
250      * can see that we are in fact already deleting the console.
251      */
252     LeaveCriticalSection(&Console->Lock);
253 
254     /* Deregister the terminal */
255     DPRINT("Deregister terminal\n");
256     ConDrvDetachTerminal(Console);
257     DPRINT("Terminal deregistered\n");
258 
259     /***
260      * Check that the console is in terminating state before continuing
261      * (the cleanup code must not change the state of the console...
262      * ...unless to cancel console deletion ?).
263      ***/
264 
265     if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_TERMINATING, TRUE))
266     {
267         return;
268     }
269 
270     /* We are now in destruction */
271     Console->State = CONSOLE_IN_DESTRUCTION;
272 
273     /* We really delete the console. Reset the count to be sure. */
274     Console->ReferenceCount = 0;
275 
276     /* Delete the last screen buffer */
277     ConDrvDeleteScreenBuffer(Console->ActiveBuffer);
278     Console->ActiveBuffer = NULL;
279     if (!IsListEmpty(&Console->BufferList))
280     {
281         /***ConDrvUnlockConsoleList();***/
282         ASSERTMSG("BUGBUGBUG!! screen buffer list not empty\n", FALSE);
283     }
284 
285     /* Deinitialize the input buffer */
286     ConDrvDeinitInputBuffer(Console);
287 
288     Console->ConsolePaused = FALSE;
289 
290     DPRINT("ConDrvDeleteConsole - Unlocking\n");
291     LeaveCriticalSection(&Console->Lock);
292     DPRINT("ConDrvDeleteConsole - Destroying lock\n");
293     DeleteCriticalSection(&Console->Lock);
294     DPRINT("ConDrvDeleteConsole - Lock destroyed\n");
295 
296     DPRINT("ConDrvDeleteConsole - Console destroyed\n");
297 }
298 
299 
300 /* PUBLIC DRIVER APIS *********************************************************/
301 
302 VOID NTAPI
303 ConDrvPause(PCONSOLE Console)
304 {
305     /* In case we are already paused, just exit... */
306     if (Console->ConsolePaused) return;
307 
308     /* ... otherwise set the flag */
309     Console->ConsolePaused = TRUE;
310 }
311 
312 VOID NTAPI
313 ConDrvUnpause(PCONSOLE Console)
314 {
315     /* In case we are already unpaused, just exit... */
316     if (!Console->ConsolePaused) return;
317 
318     /* ... otherwise reset the flag */
319     Console->ConsolePaused = FALSE;
320 }
321 
322 NTSTATUS NTAPI
323 ConDrvGetConsoleMode(IN PCONSOLE Console,
324                      IN PCONSOLE_IO_OBJECT Object,
325                      OUT PULONG ConsoleMode)
326 {
327     NTSTATUS Status = STATUS_SUCCESS;
328 
329     if (Console == NULL || Object == NULL || ConsoleMode == NULL)
330         return STATUS_INVALID_PARAMETER;
331 
332     /* Validity check */
333     ASSERT(Console == Object->Console);
334 
335     /*** FIXME: */ *ConsoleMode = 0; /***/
336 
337     if (INPUT_BUFFER == Object->Type)
338     {
339         PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
340         *ConsoleMode = InputBuffer->Mode;
341     }
342     else if (TEXTMODE_BUFFER == Object->Type || GRAPHICS_BUFFER == Object->Type)
343     {
344         PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
345         *ConsoleMode = Buffer->Mode;
346     }
347     else
348     {
349         Status = STATUS_INVALID_HANDLE;
350     }
351 
352     return Status;
353 }
354 
355 NTSTATUS NTAPI
356 ConDrvSetConsoleMode(IN PCONSOLE Console,
357                      IN PCONSOLE_IO_OBJECT Object,
358                      IN ULONG ConsoleMode)
359 {
360 #define CONSOLE_VALID_INPUT_MODES   ( ENABLE_PROCESSED_INPUT  | ENABLE_LINE_INPUT   | \
361                                       ENABLE_ECHO_INPUT       | ENABLE_WINDOW_INPUT | \
362                                       ENABLE_MOUSE_INPUT )
363 #define CONSOLE_VALID_OUTPUT_MODES  ( ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT )
364 
365     NTSTATUS Status = STATUS_SUCCESS;
366 
367     if (Console == NULL || Object == NULL)
368         return STATUS_INVALID_PARAMETER;
369 
370     /* Validity check */
371     ASSERT(Console == Object->Console);
372 
373     if (INPUT_BUFFER == Object->Type)
374     {
375         PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
376 
377         /* Only the presence of valid mode flags is allowed */
378         if (ConsoleMode & ~CONSOLE_VALID_INPUT_MODES)
379         {
380             Status = STATUS_INVALID_PARAMETER;
381         }
382         else
383         {
384             InputBuffer->Mode = (ConsoleMode & CONSOLE_VALID_INPUT_MODES);
385         }
386     }
387     else if (TEXTMODE_BUFFER == Object->Type || GRAPHICS_BUFFER == Object->Type)
388     {
389         PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
390 
391         /* Only the presence of valid mode flags is allowed */
392         if (ConsoleMode & ~CONSOLE_VALID_OUTPUT_MODES)
393         {
394             Status = STATUS_INVALID_PARAMETER;
395         }
396         else
397         {
398             Buffer->Mode = (ConsoleMode & CONSOLE_VALID_OUTPUT_MODES);
399         }
400     }
401     else
402     {
403         Status = STATUS_INVALID_HANDLE;
404     }
405 
406     return Status;
407 }
408 
409 NTSTATUS NTAPI
410 ConDrvGetConsoleCP(IN PCONSOLE Console,
411                    OUT PUINT CodePage,
412                    IN BOOLEAN OutputCP)
413 {
414     if (Console == NULL || CodePage == NULL)
415         return STATUS_INVALID_PARAMETER;
416 
417     *CodePage = (OutputCP ? Console->OutputCodePage : Console->InputCodePage);
418 
419     return STATUS_SUCCESS;
420 }
421 
422 NTSTATUS NTAPI
423 ConDrvSetConsoleCP(IN PCONSOLE Console,
424                    IN UINT CodePage,
425                    IN BOOLEAN OutputCP)
426 {
427     if (Console == NULL || !IsValidCodePage(CodePage))
428         return STATUS_INVALID_PARAMETER;
429 
430     if (OutputCP)
431     {
432         Console->OutputCodePage = CodePage;
433         Console->IsCJK = IsCJKCodePage(CodePage);
434     }
435     else
436     {
437         Console->InputCodePage = CodePage;
438     }
439 
440     return STATUS_SUCCESS;
441 }
442 
443 /* EOF */
444