Sync with trunk r65656.
[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: 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 */
10
11 /* INCLUDES *******************************************************************/
12
13 #include <consrv.h>
14
15 #include <coninput.h>
16
17 #define NDEBUG
18 #include <debug.h>
19
20
21 /* GLOBALS ********************************************************************/
22
23 static ULONG CurrentConsoleID = 0;
24
25 /* Linked list of consoles */
26 static LIST_ENTRY ConsoleList;
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 InsertConsole(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(&ConsoleList, &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(&ConsoleList);
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 * Fix the screen buffer size if needed. The rule is:
189 * ScreenBufferSize >= ConsoleSize
190 */
191 if (ConsoleInfo->ScreenBufferSize.X < ConsoleInfo->ConsoleSize.X)
192 ConsoleInfo->ScreenBufferSize.X = ConsoleInfo->ConsoleSize.X;
193 if (ConsoleInfo->ScreenBufferSize.Y < ConsoleInfo->ConsoleSize.Y)
194 ConsoleInfo->ScreenBufferSize.Y = ConsoleInfo->ConsoleSize.Y;
195
196 /*
197 * Initialize the console
198 */
199 Console->State = CONSOLE_INITIALIZING;
200 Console->ReferenceCount = 0;
201 InitializeCriticalSection(&Console->Lock);
202
203 /* Initialize the terminal interface */
204 ResetTerminal(Console);
205
206 Console->ConsoleSize = ConsoleInfo->ConsoleSize;
207 Console->FixedSize = FALSE; // Value by default; is reseted by the terminals if needed.
208
209 /* Initialize the input buffer */
210 Status = ConDrvInitInputBuffer(Console, 0 /* ConsoleInfo->InputBufferSize */);
211 if (!NT_SUCCESS(Status))
212 {
213 DPRINT1("ConDrvInitInputBuffer: failed, Status = 0x%08lx\n", Status);
214 DeleteCriticalSection(&Console->Lock);
215 ConsoleFreeHeap(Console);
216 return Status;
217 }
218
219 /* Set-up the code page */
220 Console->InputCodePage = Console->OutputCodePage = ConsoleInfo->CodePage;
221
222 /* Initialize a new text-mode screen buffer with default settings */
223 ScreenBufferInfo.ScreenBufferSize = ConsoleInfo->ScreenBufferSize;
224 ScreenBufferInfo.ScreenAttrib = ConsoleInfo->ScreenAttrib;
225 ScreenBufferInfo.PopupAttrib = ConsoleInfo->PopupAttrib;
226 ScreenBufferInfo.IsCursorVisible = TRUE;
227 ScreenBufferInfo.CursorSize = ConsoleInfo->CursorSize;
228
229 InitializeListHead(&Console->BufferList);
230 Status = ConDrvCreateScreenBuffer(&NewBuffer,
231 Console,
232 NULL,
233 CONSOLE_TEXTMODE_BUFFER,
234 &ScreenBufferInfo);
235 if (!NT_SUCCESS(Status))
236 {
237 DPRINT1("ConDrvCreateScreenBuffer: failed, Status = 0x%08lx\n", Status);
238 ConDrvDeinitInputBuffer(Console);
239 DeleteCriticalSection(&Console->Lock);
240 ConsoleFreeHeap(Console);
241 return Status;
242 }
243 /* Make the new screen buffer active */
244 Console->ActiveBuffer = NewBuffer;
245 Console->UnpauseEvent = NULL;
246
247 DPRINT("Console initialized\n");
248
249 /* All went right, so add the console to the list */
250 Status = InsertConsole(Console);
251 if (!NT_SUCCESS(Status))
252 {
253 /* Fail */
254 ConDrvDeleteConsole(Console);
255 return Status;
256 }
257
258 /* The initialization is finished */
259 DPRINT("Change state\n");
260 Console->State = CONSOLE_RUNNING;
261
262 /* Return the newly created console to the caller and a success code too */
263 *NewConsole = Console;
264 return STATUS_SUCCESS;
265 }
266
267 NTSTATUS NTAPI
268 ConDrvRegisterTerminal(IN PCONSOLE Console,
269 IN PTERMINAL Terminal)
270 {
271 NTSTATUS Status;
272
273 if (Console == NULL || Terminal == NULL)
274 return STATUS_INVALID_PARAMETER;
275
276 /* FIXME: Lock the console before ?? */
277
278 /*
279 * Attach the terminal to the console. Use now the TermIFace of the console,
280 * and not the user-defined temporary Terminal pointer.
281 */
282 Console->TermIFace = *Terminal;
283 Console->TermIFace.Console = Console;
284
285 /* Initialize the terminal AFTER having attached it to the console */
286 DPRINT("Finish initialization of terminal\n");
287 Status = Console->TermIFace.Vtbl->InitTerminal(&Console->TermIFace, Console);
288 if (!NT_SUCCESS(Status))
289 {
290 DPRINT1("Terminal initialization failed, Status = 0x%08lx\n", Status);
291
292 /* We failed, detach the terminal from the console */
293 Terminal->Console = NULL; // For the caller
294 ResetTerminal(Console);
295 return Status;
296 }
297
298 /* Copy buffer contents to screen */
299 // Terminal.Draw();
300
301 DPRINT("Terminal initialization done\n");
302 return STATUS_SUCCESS;
303 }
304
305 NTSTATUS NTAPI
306 ConDrvDeregisterTerminal(IN PCONSOLE Console)
307 {
308 if (Console == NULL) return STATUS_INVALID_PARAMETER;
309
310 /* FIXME: Lock the console before ?? */
311
312 /* Deinitialize the terminal BEFORE detaching it from the console */
313 Console->TermIFace.Vtbl->DeinitTerminal(&Console->TermIFace/*, Console*/);
314
315 /*
316 * Detach the terminal from the console:
317 * reinitialize the terminal interface.
318 */
319 ResetTerminal(Console);
320
321 DPRINT("Terminal unregistered\n");
322 return STATUS_SUCCESS;
323 }
324
325 VOID NTAPI
326 ConDrvDeleteConsole(IN PCONSOLE Console)
327 {
328 DPRINT("ConDrvDeleteConsole(0x%p)\n", Console);
329
330 /*
331 * Forbid validation of any console by other threads
332 * during the deletion of this console.
333 */
334 ConDrvLockConsoleListExclusive();
335
336 /*
337 * If the console is already being destroyed, i.e. not running
338 * or finishing to be initialized, just return.
339 */
340 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE) &&
341 !ConDrvValidateConsoleUnsafe(Console, CONSOLE_INITIALIZING, TRUE))
342 {
343 /* Unlock the console list and return */
344 ConDrvUnlockConsoleList();
345 return;
346 }
347
348 /*
349 * We are about to be destroyed. Signal it to other people
350 * so that they can terminate what they are doing, and that
351 * they cannot longer validate the console.
352 */
353 Console->State = CONSOLE_TERMINATING;
354
355 /*
356 * Allow other threads to finish their job: basically, unlock
357 * all other calls to EnterCriticalSection(&Console->Lock); by
358 * ConDrvValidateConsoleUnsafe functions so that they just see
359 * that we are not in CONSOLE_RUNNING state anymore, or unlock
360 * other concurrent calls to ConDrvDeleteConsole so that they
361 * can see that we are in fact already deleting the console.
362 */
363 LeaveCriticalSection(&Console->Lock);
364 ConDrvUnlockConsoleList();
365
366 /* Deregister the terminal */
367 DPRINT("Deregister terminal\n");
368 ConDrvDeregisterTerminal(Console);
369 DPRINT("Terminal deregistered\n");
370
371 /***
372 * Check that the console is in terminating state before continuing
373 * (the cleanup code must not change the state of the console...
374 * ...unless to cancel console deletion ?).
375 ***/
376
377 ConDrvLockConsoleListExclusive();
378
379 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_TERMINATING, TRUE))
380 {
381 ConDrvUnlockConsoleList();
382 return;
383 }
384
385 /* We are now in destruction */
386 Console->State = CONSOLE_IN_DESTRUCTION;
387
388 /* We really delete the console. Reset the count to be sure. */
389 Console->ReferenceCount = 0;
390
391 /* Remove the console from the list */
392 RemoveConsole(Console);
393
394 /* Delete the last screen buffer */
395 ConDrvDeleteScreenBuffer(Console->ActiveBuffer);
396 Console->ActiveBuffer = NULL;
397 if (!IsListEmpty(&Console->BufferList))
398 {
399 /***ConDrvUnlockConsoleList();***/
400 ASSERTMSG("BUGBUGBUG!! screen buffer list not empty\n", FALSE);
401 }
402
403 /* Deinitialize the input buffer */
404 ConDrvDeinitInputBuffer(Console);
405
406 if (Console->UnpauseEvent) CloseHandle(Console->UnpauseEvent);
407
408 DPRINT("ConDrvDeleteConsole - Unlocking\n");
409 LeaveCriticalSection(&Console->Lock);
410 DPRINT("ConDrvDeleteConsole - Destroying lock\n");
411 DeleteCriticalSection(&Console->Lock);
412 DPRINT("ConDrvDeleteConsole - Lock destroyed ; freeing console\n");
413
414 ConsoleFreeHeap(Console);
415 DPRINT("ConDrvDeleteConsole - Console destroyed\n");
416
417 /* Unlock the console list and return */
418 ConDrvUnlockConsoleList();
419 }
420
421
422 /* PUBLIC DRIVER APIS *********************************************************/
423
424 NTSTATUS NTAPI
425 ConDrvGetConsoleMode(IN PCONSOLE Console,
426 IN PCONSOLE_IO_OBJECT Object,
427 OUT PULONG ConsoleMode)
428 {
429 NTSTATUS Status = STATUS_SUCCESS;
430
431 if (Console == NULL || Object == NULL || ConsoleMode == NULL)
432 return STATUS_INVALID_PARAMETER;
433
434 /* Validity check */
435 ASSERT(Console == Object->Console);
436
437 /*** FIXME: */ *ConsoleMode = 0; /***/
438
439 if (INPUT_BUFFER == Object->Type)
440 {
441 PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
442 *ConsoleMode = InputBuffer->Mode;
443 }
444 else if (TEXTMODE_BUFFER == Object->Type || GRAPHICS_BUFFER == Object->Type)
445 {
446 PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
447 *ConsoleMode = Buffer->Mode;
448 }
449 else
450 {
451 Status = STATUS_INVALID_HANDLE;
452 }
453
454 return Status;
455 }
456
457 NTSTATUS NTAPI
458 ConDrvSetConsoleMode(IN PCONSOLE Console,
459 IN PCONSOLE_IO_OBJECT Object,
460 IN ULONG ConsoleMode)
461 {
462 #define CONSOLE_VALID_INPUT_MODES ( ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | \
463 ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT | \
464 ENABLE_MOUSE_INPUT )
465 #define CONSOLE_VALID_OUTPUT_MODES ( ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT )
466
467 NTSTATUS Status = STATUS_SUCCESS;
468
469 if (Console == NULL || Object == NULL)
470 return STATUS_INVALID_PARAMETER;
471
472 /* Validity check */
473 ASSERT(Console == Object->Console);
474
475 if (INPUT_BUFFER == Object->Type)
476 {
477 PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
478
479 /* Only the presence of valid mode flags is allowed */
480 if (ConsoleMode & ~CONSOLE_VALID_INPUT_MODES)
481 {
482 Status = STATUS_INVALID_PARAMETER;
483 }
484 else
485 {
486 InputBuffer->Mode = (ConsoleMode & CONSOLE_VALID_INPUT_MODES);
487 }
488 }
489 else if (TEXTMODE_BUFFER == Object->Type || GRAPHICS_BUFFER == Object->Type)
490 {
491 PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
492
493 /* Only the presence of valid mode flags is allowed */
494 if (ConsoleMode & ~CONSOLE_VALID_OUTPUT_MODES)
495 {
496 Status = STATUS_INVALID_PARAMETER;
497 }
498 else
499 {
500 Buffer->Mode = (ConsoleMode & CONSOLE_VALID_OUTPUT_MODES);
501 }
502 }
503 else
504 {
505 Status = STATUS_INVALID_HANDLE;
506 }
507
508 return Status;
509 }
510
511 NTSTATUS NTAPI
512 ConDrvGetConsoleCP(IN PCONSOLE Console,
513 OUT PUINT CodePage,
514 IN BOOLEAN OutputCP)
515 {
516 if (Console == NULL || CodePage == NULL)
517 return STATUS_INVALID_PARAMETER;
518
519 *CodePage = (OutputCP ? Console->OutputCodePage : Console->InputCodePage);
520
521 return STATUS_SUCCESS;
522 }
523
524 NTSTATUS NTAPI
525 ConDrvSetConsoleCP(IN PCONSOLE Console,
526 IN UINT CodePage,
527 IN BOOLEAN OutputCP)
528 {
529 if (Console == NULL || !IsValidCodePage(CodePage))
530 return STATUS_INVALID_PARAMETER;
531
532 if (OutputCP)
533 Console->OutputCodePage = CodePage;
534 else
535 Console->InputCodePage = CodePage;
536
537 return STATUS_SUCCESS;
538 }
539
540 /* EOF */