e763a812553fe20b2cd9a70b2ae8019e1128d49e
[reactos.git] / win32ss / user / winsrv / consrv / condrv / console.c
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 /* GLOBALS ********************************************************************/
22
23 static ULONG CurrentConsoleID = 0;
24
25 /* Linked list of consoles */
26 static LIST_ENTRY ConDrvConsoleList;
27 static RTL_RESOURCE ListLock;
28
29 #define ConDrvLockConsoleListExclusive() \
30 RtlAcquireResourceExclusive(&ListLock, TRUE)
31
32 #define ConDrvLockConsoleListShared() \
33 RtlAcquireResourceShared(&ListLock, TRUE)
34
35 #define ConDrvUnlockConsoleList() \
36 RtlReleaseResource(&ListLock)
37
38
39 static NTSTATUS
40 ConDrvInsertConsole(IN PCONSOLE Console)
41 {
42 ASSERT(Console);
43
44 /* All went right, so add the console to the list */
45 ConDrvLockConsoleListExclusive();
46
47 DPRINT("Insert in the list\n");
48 InsertTailList(&ConDrvConsoleList, &Console->ListEntry);
49
50 // FIXME: Move this code to the caller function!!
51 /* Get a new console ID */
52 _InterlockedExchange((PLONG)&Console->ConsoleID, CurrentConsoleID);
53 _InterlockedIncrement((PLONG)&CurrentConsoleID);
54
55 /* Unlock the console list and return success */
56 ConDrvUnlockConsoleList();
57 return STATUS_SUCCESS;
58 }
59
60 static NTSTATUS
61 RemoveConsole(IN PCONSOLE Console)
62 {
63 // ASSERT(Console);
64 if (!Console) return STATUS_INVALID_PARAMETER;
65
66 /* Remove the console from the list */
67 ConDrvLockConsoleListExclusive();
68
69 RemoveEntryList(&Console->ListEntry);
70
71 /* Unlock the console list and return success */
72 ConDrvUnlockConsoleList();
73 return STATUS_SUCCESS;
74 }
75
76
77 /* PRIVATE FUNCTIONS **********************************************************/
78
79 VOID NTAPI
80 ConDrvPause(PCONSOLE Console)
81 {
82 /* In case we already have a pause event, just exit... */
83 if (Console->UnpauseEvent) return;
84
85 /* ... otherwise create it */
86 NtCreateEvent(&Console->UnpauseEvent, EVENT_ALL_ACCESS,
87 NULL, NotificationEvent, FALSE);
88 }
89
90 VOID NTAPI
91 ConDrvUnpause(PCONSOLE Console)
92 {
93 /* In case we already freed the event, just exit... */
94 if (!Console->UnpauseEvent) return;
95
96 /* ... otherwise set and free it */
97 NtSetEvent(Console->UnpauseEvent, NULL);
98 NtClose(Console->UnpauseEvent);
99 Console->UnpauseEvent = NULL;
100 }
101
102
103 /*
104 * Console accessibility check helpers
105 */
106
107 BOOLEAN NTAPI
108 ConDrvValidateConsoleState(IN PCONSOLE Console,
109 IN CONSOLE_STATE ExpectedState)
110 {
111 // if (!Console) return FALSE;
112
113 /* The console must be locked */
114 // ASSERT(Console_locked);
115
116 return (Console->State == ExpectedState);
117 }
118
119 BOOLEAN NTAPI
120 ConDrvValidateConsoleUnsafe(IN PCONSOLE Console,
121 IN CONSOLE_STATE ExpectedState,
122 IN BOOLEAN LockConsole)
123 {
124 if (!Console) return FALSE;
125
126 /*
127 * Lock the console to forbid possible console's state changes
128 * (which must be done when the console is already locked).
129 * If we don't want to lock it, it's because the lock is already
130 * held. So there must be no problems.
131 */
132 if (LockConsole) EnterCriticalSection(&Console->Lock);
133
134 // ASSERT(Console_locked);
135
136 /* Check whether the console's state is what we expect */
137 if (!ConDrvValidateConsoleState(Console, ExpectedState))
138 {
139 if (LockConsole) LeaveCriticalSection(&Console->Lock);
140 return FALSE;
141 }
142
143 return TRUE;
144 }
145
146
147 /* CONSOLE INITIALIZATION FUNCTIONS *******************************************/
148
149 VOID NTAPI
150 ConDrvInitConsoleSupport(VOID)
151 {
152 DPRINT("CONSRV: ConDrvInitConsoleSupport()\n");
153
154 /* Initialize the console list and its lock */
155 InitializeListHead(&ConDrvConsoleList);
156 RtlInitializeResource(&ListLock);
157 }
158
159 /* For resetting the terminal - defined in dummyterm.c */
160 VOID ResetTerminal(IN PCONSOLE Console);
161
162 NTSTATUS NTAPI
163 ConDrvInitConsole(OUT PCONSOLE* NewConsole,
164 IN PCONSOLE_INFO ConsoleInfo)
165 {
166 NTSTATUS Status;
167 // CONSOLE_INFO CapturedConsoleInfo;
168 TEXTMODE_BUFFER_INFO ScreenBufferInfo;
169 PCONSOLE Console;
170 PCONSOLE_SCREEN_BUFFER NewBuffer;
171
172 if (NewConsole == NULL || ConsoleInfo == NULL)
173 return STATUS_INVALID_PARAMETER;
174
175 *NewConsole = NULL;
176
177 /*
178 * Allocate a new console
179 */
180 Console = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(*Console));
181 if (NULL == Console)
182 {
183 DPRINT1("Not enough memory for console creation.\n");
184 return STATUS_NO_MEMORY;
185 }
186
187 /*
188 * Set and fix the screen buffer size if needed.
189 * The rule is: ScreenBufferSize >= ConsoleSize
190 */
191 if (ConsoleInfo->ScreenBufferSize.X == 0) ConsoleInfo->ScreenBufferSize.X = 1;
192 if (ConsoleInfo->ScreenBufferSize.Y == 0) ConsoleInfo->ScreenBufferSize.Y = 1;
193 if (ConsoleInfo->ScreenBufferSize.X < ConsoleInfo->ConsoleSize.X)
194 ConsoleInfo->ScreenBufferSize.X = ConsoleInfo->ConsoleSize.X;
195 if (ConsoleInfo->ScreenBufferSize.Y < ConsoleInfo->ConsoleSize.Y)
196 ConsoleInfo->ScreenBufferSize.Y = ConsoleInfo->ConsoleSize.Y;
197
198 /*
199 * Initialize the console
200 */
201 Console->State = CONSOLE_INITIALIZING;
202 Console->ReferenceCount = 0;
203 InitializeCriticalSection(&Console->Lock);
204
205 /* Initialize the terminal interface */
206 ResetTerminal(Console);
207
208 Console->ConsoleSize = ConsoleInfo->ConsoleSize;
209 Console->FixedSize = FALSE; // Value by default; is reseted by the terminals if needed.
210
211 /* Initialize the input buffer */
212 Status = ConDrvInitInputBuffer(Console, 0 /* ConsoleInfo->InputBufferSize */);
213 if (!NT_SUCCESS(Status))
214 {
215 DPRINT1("ConDrvInitInputBuffer: failed, Status = 0x%08lx\n", Status);
216 DeleteCriticalSection(&Console->Lock);
217 ConsoleFreeHeap(Console);
218 return Status;
219 }
220
221 /* Set-up the code page */
222 if (IsValidCodePage(ConsoleInfo->CodePage))
223 Console->InputCodePage = Console->OutputCodePage = ConsoleInfo->CodePage;
224
225 Console->IsCJK = IsCJKCodePage(Console->OutputCodePage);
226
227 /* Initialize a new text-mode screen buffer with default settings */
228 ScreenBufferInfo.ScreenBufferSize = ConsoleInfo->ScreenBufferSize;
229 ScreenBufferInfo.ViewSize = ConsoleInfo->ConsoleSize;
230 ScreenBufferInfo.ScreenAttrib = ConsoleInfo->ScreenAttrib;
231 ScreenBufferInfo.PopupAttrib = ConsoleInfo->PopupAttrib;
232 ScreenBufferInfo.CursorSize = ConsoleInfo->CursorSize;
233 ScreenBufferInfo.IsCursorVisible = TRUE;
234
235 InitializeListHead(&Console->BufferList);
236 Status = ConDrvCreateScreenBuffer(&NewBuffer,
237 Console,
238 NULL,
239 CONSOLE_TEXTMODE_BUFFER,
240 &ScreenBufferInfo);
241 if (!NT_SUCCESS(Status))
242 {
243 DPRINT1("ConDrvCreateScreenBuffer: failed, Status = 0x%08lx\n", Status);
244 ConDrvDeinitInputBuffer(Console);
245 DeleteCriticalSection(&Console->Lock);
246 ConsoleFreeHeap(Console);
247 return Status;
248 }
249 /* Make the new screen buffer active */
250 Console->ActiveBuffer = NewBuffer;
251 Console->UnpauseEvent = NULL;
252
253 DPRINT("Console initialized\n");
254
255 /* All went right, so add the console to the list */
256 Status = ConDrvInsertConsole(Console);
257 if (!NT_SUCCESS(Status))
258 {
259 /* Fail */
260 ConDrvDeleteConsole(Console);
261 return Status;
262 }
263
264 /* The initialization is finished */
265 DPRINT("Change state\n");
266 Console->State = CONSOLE_RUNNING;
267
268 /* Return the newly created console to the caller and a success code too */
269 *NewConsole = Console;
270 return STATUS_SUCCESS;
271 }
272
273 NTSTATUS NTAPI
274 ConDrvAttachTerminal(IN PCONSOLE Console,
275 IN PTERMINAL Terminal)
276 {
277 NTSTATUS Status;
278
279 if (Console == NULL || Terminal == NULL)
280 return STATUS_INVALID_PARAMETER;
281
282 /* FIXME: Lock the console before ?? */
283
284 /*
285 * Attach the terminal to the console. Use now the TermIFace of the console,
286 * and not the user-defined temporary Terminal pointer.
287 */
288 Console->TermIFace = *Terminal;
289 Console->TermIFace.Console = Console;
290
291 /* Initialize the terminal AFTER having attached it to the console */
292 DPRINT("Finish initialization of terminal\n");
293 Status = Console->TermIFace.Vtbl->InitTerminal(&Console->TermIFace, Console);
294 if (!NT_SUCCESS(Status))
295 {
296 DPRINT1("Terminal initialization failed, Status = 0x%08lx\n", Status);
297
298 /* We failed, detach the terminal from the console */
299 Terminal->Console = NULL; // For the caller
300 ResetTerminal(Console);
301 return Status;
302 }
303
304 /* Copy buffer contents to screen */
305 // Terminal.Draw();
306
307 DPRINT("Terminal initialization done\n");
308 return STATUS_SUCCESS;
309 }
310
311 NTSTATUS NTAPI
312 ConDrvDetachTerminal(IN PCONSOLE Console)
313 {
314 if (Console == NULL) return STATUS_INVALID_PARAMETER;
315
316 /* FIXME: Lock the console before ?? */
317
318 /* Deinitialize the terminal BEFORE detaching it from the console */
319 Console->TermIFace.Vtbl->DeinitTerminal(&Console->TermIFace/*, Console*/);
320
321 /*
322 * Detach the terminal from the console:
323 * reinitialize the terminal interface.
324 */
325 ResetTerminal(Console);
326
327 DPRINT("Terminal unregistered\n");
328 return STATUS_SUCCESS;
329 }
330
331 VOID NTAPI
332 ConDrvDeleteConsole(IN PCONSOLE Console)
333 {
334 DPRINT("ConDrvDeleteConsole(0x%p)\n", Console);
335
336 /*
337 * Forbid validation of any console by other threads
338 * during the deletion of this console.
339 */
340 ConDrvLockConsoleListExclusive();
341
342 /*
343 * If the console is already being destroyed, i.e. not running
344 * or finishing to be initialized, just return.
345 */
346 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE) &&
347 !ConDrvValidateConsoleUnsafe(Console, CONSOLE_INITIALIZING, TRUE))
348 {
349 /* Unlock the console list and return */
350 ConDrvUnlockConsoleList();
351 return;
352 }
353
354 /*
355 * We are about to be destroyed. Signal it to other people
356 * so that they can terminate what they are doing, and that
357 * they cannot longer validate the console.
358 */
359 Console->State = CONSOLE_TERMINATING;
360
361 /*
362 * Allow other threads to finish their job: basically, unlock
363 * all other calls to EnterCriticalSection(&Console->Lock); by
364 * ConDrvValidateConsoleUnsafe functions so that they just see
365 * that we are not in CONSOLE_RUNNING state anymore, or unlock
366 * other concurrent calls to ConDrvDeleteConsole so that they
367 * can see that we are in fact already deleting the console.
368 */
369 LeaveCriticalSection(&Console->Lock);
370 ConDrvUnlockConsoleList();
371
372 /* Deregister the terminal */
373 DPRINT("Deregister terminal\n");
374 ConDrvDetachTerminal(Console);
375 DPRINT("Terminal deregistered\n");
376
377 /***
378 * Check that the console is in terminating state before continuing
379 * (the cleanup code must not change the state of the console...
380 * ...unless to cancel console deletion ?).
381 ***/
382
383 ConDrvLockConsoleListExclusive();
384
385 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_TERMINATING, TRUE))
386 {
387 ConDrvUnlockConsoleList();
388 return;
389 }
390
391 /* We are now in destruction */
392 Console->State = CONSOLE_IN_DESTRUCTION;
393
394 /* We really delete the console. Reset the count to be sure. */
395 Console->ReferenceCount = 0;
396
397 /* Remove the console from the list */
398 RemoveConsole(Console);
399
400 /* Delete the last screen buffer */
401 ConDrvDeleteScreenBuffer(Console->ActiveBuffer);
402 Console->ActiveBuffer = NULL;
403 if (!IsListEmpty(&Console->BufferList))
404 {
405 /***ConDrvUnlockConsoleList();***/
406 ASSERTMSG("BUGBUGBUG!! screen buffer list not empty\n", FALSE);
407 }
408
409 /* Deinitialize the input buffer */
410 ConDrvDeinitInputBuffer(Console);
411
412 if (Console->UnpauseEvent) NtClose(Console->UnpauseEvent);
413
414 DPRINT("ConDrvDeleteConsole - Unlocking\n");
415 LeaveCriticalSection(&Console->Lock);
416 DPRINT("ConDrvDeleteConsole - Destroying lock\n");
417 DeleteCriticalSection(&Console->Lock);
418 DPRINT("ConDrvDeleteConsole - Lock destroyed ; freeing console\n");
419
420 ConsoleFreeHeap(Console);
421 DPRINT("ConDrvDeleteConsole - Console destroyed\n");
422
423 /* Unlock the console list and return */
424 ConDrvUnlockConsoleList();
425 }
426
427
428 /* PUBLIC DRIVER APIS *********************************************************/
429
430 NTSTATUS NTAPI
431 ConDrvGetConsoleMode(IN PCONSOLE Console,
432 IN PCONSOLE_IO_OBJECT Object,
433 OUT PULONG ConsoleMode)
434 {
435 NTSTATUS Status = STATUS_SUCCESS;
436
437 if (Console == NULL || Object == NULL || ConsoleMode == NULL)
438 return STATUS_INVALID_PARAMETER;
439
440 /* Validity check */
441 ASSERT(Console == Object->Console);
442
443 /*** FIXME: */ *ConsoleMode = 0; /***/
444
445 if (INPUT_BUFFER == Object->Type)
446 {
447 PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
448 *ConsoleMode = InputBuffer->Mode;
449 }
450 else if (TEXTMODE_BUFFER == Object->Type || GRAPHICS_BUFFER == Object->Type)
451 {
452 PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
453 *ConsoleMode = Buffer->Mode;
454 }
455 else
456 {
457 Status = STATUS_INVALID_HANDLE;
458 }
459
460 return Status;
461 }
462
463 NTSTATUS NTAPI
464 ConDrvSetConsoleMode(IN PCONSOLE Console,
465 IN PCONSOLE_IO_OBJECT Object,
466 IN ULONG ConsoleMode)
467 {
468 #define CONSOLE_VALID_INPUT_MODES ( ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | \
469 ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT | \
470 ENABLE_MOUSE_INPUT )
471 #define CONSOLE_VALID_OUTPUT_MODES ( ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT )
472
473 NTSTATUS Status = STATUS_SUCCESS;
474
475 if (Console == NULL || Object == NULL)
476 return STATUS_INVALID_PARAMETER;
477
478 /* Validity check */
479 ASSERT(Console == Object->Console);
480
481 if (INPUT_BUFFER == Object->Type)
482 {
483 PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
484
485 /* Only the presence of valid mode flags is allowed */
486 if (ConsoleMode & ~CONSOLE_VALID_INPUT_MODES)
487 {
488 Status = STATUS_INVALID_PARAMETER;
489 }
490 else
491 {
492 InputBuffer->Mode = (ConsoleMode & CONSOLE_VALID_INPUT_MODES);
493 }
494 }
495 else if (TEXTMODE_BUFFER == Object->Type || GRAPHICS_BUFFER == Object->Type)
496 {
497 PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
498
499 /* Only the presence of valid mode flags is allowed */
500 if (ConsoleMode & ~CONSOLE_VALID_OUTPUT_MODES)
501 {
502 Status = STATUS_INVALID_PARAMETER;
503 }
504 else
505 {
506 Buffer->Mode = (ConsoleMode & CONSOLE_VALID_OUTPUT_MODES);
507 }
508 }
509 else
510 {
511 Status = STATUS_INVALID_HANDLE;
512 }
513
514 return Status;
515 }
516
517 NTSTATUS NTAPI
518 ConDrvGetConsoleCP(IN PCONSOLE Console,
519 OUT PUINT CodePage,
520 IN BOOLEAN OutputCP)
521 {
522 if (Console == NULL || CodePage == NULL)
523 return STATUS_INVALID_PARAMETER;
524
525 *CodePage = (OutputCP ? Console->OutputCodePage : Console->InputCodePage);
526
527 return STATUS_SUCCESS;
528 }
529
530 NTSTATUS NTAPI
531 ConDrvSetConsoleCP(IN PCONSOLE Console,
532 IN UINT CodePage,
533 IN BOOLEAN OutputCP)
534 {
535 if (Console == NULL || !IsValidCodePage(CodePage))
536 return STATUS_INVALID_PARAMETER;
537
538 if (OutputCP)
539 {
540 Console->OutputCodePage = CodePage;
541 Console->IsCJK = IsCJKCodePage(CodePage);
542 }
543 else
544 {
545 Console->InputCodePage = CodePage;
546 }
547
548 return STATUS_SUCCESS;
549 }
550
551 /* EOF */