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
8 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
9 * Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
12 /* INCLUDES *******************************************************************/
16 #include "../../concfg/font.h"
21 /* GLOBALS ********************************************************************/
23 static ULONG CurrentConsoleID
= 0;
25 /* Linked list of consoles */
26 static LIST_ENTRY ConDrvConsoleList
;
27 static RTL_RESOURCE ListLock
;
29 #define ConDrvLockConsoleListExclusive() \
30 RtlAcquireResourceExclusive(&ListLock, TRUE)
32 #define ConDrvLockConsoleListShared() \
33 RtlAcquireResourceShared(&ListLock, TRUE)
35 #define ConDrvUnlockConsoleList() \
36 RtlReleaseResource(&ListLock)
40 ConDrvInsertConsole(IN PCONSOLE Console
)
44 /* All went right, so add the console to the list */
45 ConDrvLockConsoleListExclusive();
47 DPRINT("Insert in the list\n");
48 InsertTailList(&ConDrvConsoleList
, &Console
->ListEntry
);
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
);
55 /* Unlock the console list and return success */
56 ConDrvUnlockConsoleList();
57 return STATUS_SUCCESS
;
61 RemoveConsole(IN PCONSOLE Console
)
64 if (!Console
) return STATUS_INVALID_PARAMETER
;
66 /* Remove the console from the list */
67 ConDrvLockConsoleListExclusive();
69 RemoveEntryList(&Console
->ListEntry
);
71 /* Unlock the console list and return success */
72 ConDrvUnlockConsoleList();
73 return STATUS_SUCCESS
;
77 /* PRIVATE FUNCTIONS **********************************************************/
80 ConDrvPause(PCONSOLE Console
)
82 /* In case we already have a pause event, just exit... */
83 if (Console
->UnpauseEvent
) return;
85 /* ... otherwise create it */
86 NtCreateEvent(&Console
->UnpauseEvent
, EVENT_ALL_ACCESS
,
87 NULL
, NotificationEvent
, FALSE
);
91 ConDrvUnpause(PCONSOLE Console
)
93 /* In case we already freed the event, just exit... */
94 if (!Console
->UnpauseEvent
) return;
96 /* ... otherwise set and free it */
97 NtSetEvent(Console
->UnpauseEvent
, NULL
);
98 NtClose(Console
->UnpauseEvent
);
99 Console
->UnpauseEvent
= NULL
;
104 * Console accessibility check helpers
108 ConDrvValidateConsoleState(IN PCONSOLE Console
,
109 IN CONSOLE_STATE ExpectedState
)
111 // if (!Console) return FALSE;
113 /* The console must be locked */
114 // ASSERT(Console_locked);
116 return (Console
->State
== ExpectedState
);
120 ConDrvValidateConsoleUnsafe(IN PCONSOLE Console
,
121 IN CONSOLE_STATE ExpectedState
,
122 IN BOOLEAN LockConsole
)
124 if (!Console
) return FALSE
;
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.
132 if (LockConsole
) EnterCriticalSection(&Console
->Lock
);
134 // ASSERT(Console_locked);
136 /* Check whether the console's state is what we expect */
137 if (!ConDrvValidateConsoleState(Console
, ExpectedState
))
139 if (LockConsole
) LeaveCriticalSection(&Console
->Lock
);
147 /* CONSOLE INITIALIZATION FUNCTIONS *******************************************/
150 ConDrvInitConsoleSupport(VOID
)
152 DPRINT("CONSRV: ConDrvInitConsoleSupport()\n");
154 /* Initialize the console list and its lock */
155 InitializeListHead(&ConDrvConsoleList
);
156 RtlInitializeResource(&ListLock
);
159 /* For resetting the terminal - defined in dummyterm.c */
160 VOID
ResetTerminal(IN PCONSOLE Console
);
163 ConDrvInitConsole(OUT PCONSOLE
* NewConsole
,
164 IN PCONSOLE_INFO ConsoleInfo
)
167 // CONSOLE_INFO CapturedConsoleInfo;
168 TEXTMODE_BUFFER_INFO ScreenBufferInfo
;
170 PCONSOLE_SCREEN_BUFFER NewBuffer
;
172 if (NewConsole
== NULL
|| ConsoleInfo
== NULL
)
173 return STATUS_INVALID_PARAMETER
;
178 * Allocate a new console
180 Console
= ConsoleAllocHeap(HEAP_ZERO_MEMORY
, sizeof(*Console
));
183 DPRINT1("Not enough memory for console creation.\n");
184 return STATUS_NO_MEMORY
;
188 * Set and fix the screen buffer size if needed.
189 * The rule is: ScreenBufferSize >= ConsoleSize
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
;
199 * Initialize the console
201 Console
->State
= CONSOLE_INITIALIZING
;
202 Console
->ReferenceCount
= 0;
203 InitializeCriticalSection(&Console
->Lock
);
205 /* Initialize the terminal interface */
206 ResetTerminal(Console
);
208 Console
->ConsoleSize
= ConsoleInfo
->ConsoleSize
;
209 Console
->FixedSize
= FALSE
; // Value by default; is reseted by the terminals if needed.
211 /* Initialize the input buffer */
212 Status
= ConDrvInitInputBuffer(Console
, 0 /* ConsoleInfo->InputBufferSize */);
213 if (!NT_SUCCESS(Status
))
215 DPRINT1("ConDrvInitInputBuffer: failed, Status = 0x%08lx\n", Status
);
216 DeleteCriticalSection(&Console
->Lock
);
217 ConsoleFreeHeap(Console
);
221 /* Set-up the code page */
222 if (IsValidCodePage(ConsoleInfo
->CodePage
))
223 Console
->InputCodePage
= Console
->OutputCodePage
= ConsoleInfo
->CodePage
;
225 Console
->IsCJK
= IsCJKCodePage(Console
->OutputCodePage
);
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
;
235 InitializeListHead(&Console
->BufferList
);
236 Status
= ConDrvCreateScreenBuffer(&NewBuffer
,
239 CONSOLE_TEXTMODE_BUFFER
,
241 if (!NT_SUCCESS(Status
))
243 DPRINT1("ConDrvCreateScreenBuffer: failed, Status = 0x%08lx\n", Status
);
244 ConDrvDeinitInputBuffer(Console
);
245 DeleteCriticalSection(&Console
->Lock
);
246 ConsoleFreeHeap(Console
);
249 /* Make the new screen buffer active */
250 Console
->ActiveBuffer
= NewBuffer
;
251 Console
->UnpauseEvent
= NULL
;
253 DPRINT("Console initialized\n");
255 /* All went right, so add the console to the list */
256 Status
= ConDrvInsertConsole(Console
);
257 if (!NT_SUCCESS(Status
))
260 ConDrvDeleteConsole(Console
);
264 /* The initialization is finished */
265 DPRINT("Change state\n");
266 Console
->State
= CONSOLE_RUNNING
;
268 /* Return the newly created console to the caller and a success code too */
269 *NewConsole
= Console
;
270 return STATUS_SUCCESS
;
274 ConDrvAttachTerminal(IN PCONSOLE Console
,
275 IN PTERMINAL Terminal
)
279 if (Console
== NULL
|| Terminal
== NULL
)
280 return STATUS_INVALID_PARAMETER
;
282 /* FIXME: Lock the console before ?? */
285 * Attach the terminal to the console. Use now the TermIFace of the console,
286 * and not the user-defined temporary Terminal pointer.
288 Console
->TermIFace
= *Terminal
;
289 Console
->TermIFace
.Console
= Console
;
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
))
296 DPRINT1("Terminal initialization failed, Status = 0x%08lx\n", Status
);
298 /* We failed, detach the terminal from the console */
299 Terminal
->Console
= NULL
; // For the caller
300 ResetTerminal(Console
);
304 /* Copy buffer contents to screen */
307 DPRINT("Terminal initialization done\n");
308 return STATUS_SUCCESS
;
312 ConDrvDetachTerminal(IN PCONSOLE Console
)
314 if (Console
== NULL
) return STATUS_INVALID_PARAMETER
;
316 /* FIXME: Lock the console before ?? */
318 /* Deinitialize the terminal BEFORE detaching it from the console */
319 Console
->TermIFace
.Vtbl
->DeinitTerminal(&Console
->TermIFace
/*, Console*/);
322 * Detach the terminal from the console:
323 * reinitialize the terminal interface.
325 ResetTerminal(Console
);
327 DPRINT("Terminal unregistered\n");
328 return STATUS_SUCCESS
;
332 ConDrvDeleteConsole(IN PCONSOLE Console
)
334 DPRINT("ConDrvDeleteConsole(0x%p)\n", Console
);
337 * Forbid validation of any console by other threads
338 * during the deletion of this console.
340 ConDrvLockConsoleListExclusive();
343 * If the console is already being destroyed, i.e. not running
344 * or finishing to be initialized, just return.
346 if (!ConDrvValidateConsoleUnsafe(Console
, CONSOLE_RUNNING
, TRUE
) &&
347 !ConDrvValidateConsoleUnsafe(Console
, CONSOLE_INITIALIZING
, TRUE
))
349 /* Unlock the console list and return */
350 ConDrvUnlockConsoleList();
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.
359 Console
->State
= CONSOLE_TERMINATING
;
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.
369 LeaveCriticalSection(&Console
->Lock
);
370 ConDrvUnlockConsoleList();
372 /* Deregister the terminal */
373 DPRINT("Deregister terminal\n");
374 ConDrvDetachTerminal(Console
);
375 DPRINT("Terminal deregistered\n");
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 ?).
383 ConDrvLockConsoleListExclusive();
385 if (!ConDrvValidateConsoleUnsafe(Console
, CONSOLE_TERMINATING
, TRUE
))
387 ConDrvUnlockConsoleList();
391 /* We are now in destruction */
392 Console
->State
= CONSOLE_IN_DESTRUCTION
;
394 /* We really delete the console. Reset the count to be sure. */
395 Console
->ReferenceCount
= 0;
397 /* Remove the console from the list */
398 RemoveConsole(Console
);
400 /* Delete the last screen buffer */
401 ConDrvDeleteScreenBuffer(Console
->ActiveBuffer
);
402 Console
->ActiveBuffer
= NULL
;
403 if (!IsListEmpty(&Console
->BufferList
))
405 /***ConDrvUnlockConsoleList();***/
406 ASSERTMSG("BUGBUGBUG!! screen buffer list not empty\n", FALSE
);
409 /* Deinitialize the input buffer */
410 ConDrvDeinitInputBuffer(Console
);
412 if (Console
->UnpauseEvent
) CloseHandle(Console
->UnpauseEvent
);
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");
420 ConsoleFreeHeap(Console
);
421 DPRINT("ConDrvDeleteConsole - Console destroyed\n");
423 /* Unlock the console list and return */
424 ConDrvUnlockConsoleList();
428 /* PUBLIC DRIVER APIS *********************************************************/
431 ConDrvGetConsoleMode(IN PCONSOLE Console
,
432 IN PCONSOLE_IO_OBJECT Object
,
433 OUT PULONG ConsoleMode
)
435 NTSTATUS Status
= STATUS_SUCCESS
;
437 if (Console
== NULL
|| Object
== NULL
|| ConsoleMode
== NULL
)
438 return STATUS_INVALID_PARAMETER
;
441 ASSERT(Console
== Object
->Console
);
443 /*** FIXME: */ *ConsoleMode
= 0; /***/
445 if (INPUT_BUFFER
== Object
->Type
)
447 PCONSOLE_INPUT_BUFFER InputBuffer
= (PCONSOLE_INPUT_BUFFER
)Object
;
448 *ConsoleMode
= InputBuffer
->Mode
;
450 else if (TEXTMODE_BUFFER
== Object
->Type
|| GRAPHICS_BUFFER
== Object
->Type
)
452 PCONSOLE_SCREEN_BUFFER Buffer
= (PCONSOLE_SCREEN_BUFFER
)Object
;
453 *ConsoleMode
= Buffer
->Mode
;
457 Status
= STATUS_INVALID_HANDLE
;
464 ConDrvSetConsoleMode(IN PCONSOLE Console
,
465 IN PCONSOLE_IO_OBJECT Object
,
466 IN ULONG ConsoleMode
)
468 #define CONSOLE_VALID_INPUT_MODES ( ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | \
469 ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT | \
471 #define CONSOLE_VALID_OUTPUT_MODES ( ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT )
473 NTSTATUS Status
= STATUS_SUCCESS
;
475 if (Console
== NULL
|| Object
== NULL
)
476 return STATUS_INVALID_PARAMETER
;
479 ASSERT(Console
== Object
->Console
);
481 if (INPUT_BUFFER
== Object
->Type
)
483 PCONSOLE_INPUT_BUFFER InputBuffer
= (PCONSOLE_INPUT_BUFFER
)Object
;
485 /* Only the presence of valid mode flags is allowed */
486 if (ConsoleMode
& ~CONSOLE_VALID_INPUT_MODES
)
488 Status
= STATUS_INVALID_PARAMETER
;
492 InputBuffer
->Mode
= (ConsoleMode
& CONSOLE_VALID_INPUT_MODES
);
495 else if (TEXTMODE_BUFFER
== Object
->Type
|| GRAPHICS_BUFFER
== Object
->Type
)
497 PCONSOLE_SCREEN_BUFFER Buffer
= (PCONSOLE_SCREEN_BUFFER
)Object
;
499 /* Only the presence of valid mode flags is allowed */
500 if (ConsoleMode
& ~CONSOLE_VALID_OUTPUT_MODES
)
502 Status
= STATUS_INVALID_PARAMETER
;
506 Buffer
->Mode
= (ConsoleMode
& CONSOLE_VALID_OUTPUT_MODES
);
511 Status
= STATUS_INVALID_HANDLE
;
518 ConDrvGetConsoleCP(IN PCONSOLE Console
,
522 if (Console
== NULL
|| CodePage
== NULL
)
523 return STATUS_INVALID_PARAMETER
;
525 *CodePage
= (OutputCP
? Console
->OutputCodePage
: Console
->InputCodePage
);
527 return STATUS_SUCCESS
;
531 ConDrvSetConsoleCP(IN PCONSOLE Console
,
535 if (Console
== NULL
|| !IsValidCodePage(CodePage
))
536 return STATUS_INVALID_PARAMETER
;
540 Console
->OutputCodePage
= CodePage
;
541 Console
->IsCJK
= IsCJKCodePage(CodePage
);
545 Console
->InputCodePage
= CodePage
;
548 return STATUS_SUCCESS
;