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)
11 /* INCLUDES *******************************************************************/
21 /* GLOBALS ********************************************************************/
23 static ULONG CurrentConsoleID
= 0;
25 /* Linked list of consoles */
26 static LIST_ENTRY ConsoleList
;
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 InsertConsole(IN PCONSOLE Console
)
44 /* All went right, so add the console to the list */
45 ConDrvLockConsoleListExclusive();
47 DPRINT1("Insert in the list\n");
48 InsertTailList(&ConsoleList
, &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 **********************************************************/
79 // Adapted from reactos/lib/rtl/unicode.c, RtlCreateUnicodeString line 2180
81 ConsoleCreateUnicodeString(IN OUT PUNICODE_STRING UniDest
,
84 SIZE_T Size
= (wcslen(Source
) + 1) * sizeof(WCHAR
);
85 if (Size
> MAXUSHORT
) return FALSE
;
87 UniDest
->Buffer
= ConsoleAllocHeap(HEAP_ZERO_MEMORY
, Size
);
88 if (UniDest
->Buffer
== NULL
) return FALSE
;
90 RtlCopyMemory(UniDest
->Buffer
, Source
, Size
);
91 UniDest
->MaximumLength
= (USHORT
)Size
;
92 UniDest
->Length
= (USHORT
)Size
- sizeof(WCHAR
);
97 // Adapted from reactos/lib/rtl/unicode.c, RtlFreeUnicodeString line 431
99 ConsoleFreeUnicodeString(IN PUNICODE_STRING UnicodeString
)
101 if (UnicodeString
->Buffer
)
103 ConsoleFreeHeap(UnicodeString
->Buffer
);
104 RtlZeroMemory(UnicodeString
, sizeof(UNICODE_STRING
));
109 ConDrvPause(PCONSOLE Console
)
111 if (!Console
->UnpauseEvent
)
112 Console
->UnpauseEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
116 ConDrvUnpause(PCONSOLE Console
)
118 if (Console
->UnpauseEvent
)
120 SetEvent(Console
->UnpauseEvent
);
121 CloseHandle(Console
->UnpauseEvent
);
122 Console
->UnpauseEvent
= NULL
;
128 * Console accessibility check helpers
132 ConDrvValidateConsoleState(IN PCONSOLE Console
,
133 IN CONSOLE_STATE ExpectedState
)
135 // if (!Console) return FALSE;
137 /* The console must be locked */
138 // ASSERT(Console_locked);
140 return (Console
->State
== ExpectedState
);
144 ConDrvValidateConsoleUnsafe(IN PCONSOLE Console
,
145 IN CONSOLE_STATE ExpectedState
,
146 IN BOOLEAN LockConsole
)
148 if (!Console
) return FALSE
;
151 * Lock the console to forbid possible console's state changes
152 * (which must be done when the console is already locked).
153 * If we don't want to lock it, it's because the lock is already
154 * held. So there must be no problems.
156 if (LockConsole
) EnterCriticalSection(&Console
->Lock
);
158 // ASSERT(Console_locked);
160 /* Check whether the console's state is what we expect */
161 if (!ConDrvValidateConsoleState(Console
, ExpectedState
))
163 if (LockConsole
) LeaveCriticalSection(&Console
->Lock
);
171 /* CONSOLE INITIALIZATION FUNCTIONS *******************************************/
174 ConDrvInitConsoleSupport(VOID
)
176 DPRINT("CONSRV: ConDrvInitConsoleSupport()\n");
178 /* Initialize the console list and its lock */
179 InitializeListHead(&ConsoleList
);
180 RtlInitializeResource(&ListLock
);
182 /* Should call LoadKeyboardLayout */
185 /* For resetting the terminal - defined in dummyterm.c */
186 VOID
ResetTerminal(IN PCONSOLE Console
);
189 ConDrvInitConsole(OUT PCONSOLE
* NewConsole
,
190 IN PCONSOLE_INFO ConsoleInfo
)
193 SECURITY_ATTRIBUTES SecurityAttributes
;
194 // CONSOLE_INFO CapturedConsoleInfo;
195 TEXTMODE_BUFFER_INFO ScreenBufferInfo
;
197 PCONSOLE_SCREEN_BUFFER NewBuffer
;
199 WCHAR DefaultTitle
[128];
202 if (NewConsole
== NULL
|| ConsoleInfo
== NULL
)
203 return STATUS_INVALID_PARAMETER
;
208 * Allocate a new console
210 Console
= ConsoleAllocHeap(HEAP_ZERO_MEMORY
, sizeof(*Console
));
213 DPRINT1("Not enough memory for console creation.\n");
214 return STATUS_NO_MEMORY
;
218 * Fix the screen buffer size if needed. The rule is:
219 * ScreenBufferSize >= ConsoleSize
221 if (ConsoleInfo
->ScreenBufferSize
.X
< ConsoleInfo
->ConsoleSize
.X
)
222 ConsoleInfo
->ScreenBufferSize
.X
= ConsoleInfo
->ConsoleSize
.X
;
223 if (ConsoleInfo
->ScreenBufferSize
.Y
< ConsoleInfo
->ConsoleSize
.Y
)
224 ConsoleInfo
->ScreenBufferSize
.Y
= ConsoleInfo
->ConsoleSize
.Y
;
227 * Initialize the console
229 Console
->State
= CONSOLE_INITIALIZING
;
230 Console
->ReferenceCount
= 0;
231 InitializeCriticalSection(&Console
->Lock
);
233 /* Initialize the terminal interface */
234 ResetTerminal(Console
);
236 Console
->ConsoleSize
= ConsoleInfo
->ConsoleSize
;
237 Console
->FixedSize
= FALSE
; // Value by default; is reseted by the terminals if needed.
240 * Initialize the input buffer
242 ConSrvInitObject(&Console
->InputBuffer
.Header
, INPUT_BUFFER
, Console
);
244 SecurityAttributes
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
245 SecurityAttributes
.lpSecurityDescriptor
= NULL
;
246 SecurityAttributes
.bInheritHandle
= TRUE
;
247 Console
->InputBuffer
.ActiveEvent
= CreateEventW(&SecurityAttributes
, TRUE
, FALSE
, NULL
);
248 if (NULL
== Console
->InputBuffer
.ActiveEvent
)
250 DeleteCriticalSection(&Console
->Lock
);
251 ConsoleFreeHeap(Console
);
252 return STATUS_UNSUCCESSFUL
;
255 Console
->InputBuffer
.InputBufferSize
= 0; // FIXME!
256 InitializeListHead(&Console
->InputBuffer
.InputEvents
);
257 Console
->InputBuffer
.Mode
= ENABLE_PROCESSED_INPUT
| ENABLE_LINE_INPUT
|
258 ENABLE_ECHO_INPUT
| ENABLE_MOUSE_INPUT
;
260 /* Set-up the code page */
261 Console
->InputCodePage
= Console
->OutputCodePage
= ConsoleInfo
->CodePage
;
263 /* Initialize a new text-mode screen buffer with default settings */
264 ScreenBufferInfo
.ScreenBufferSize
= ConsoleInfo
->ScreenBufferSize
;
265 ScreenBufferInfo
.ScreenAttrib
= ConsoleInfo
->ScreenAttrib
;
266 ScreenBufferInfo
.PopupAttrib
= ConsoleInfo
->PopupAttrib
;
267 ScreenBufferInfo
.IsCursorVisible
= TRUE
;
268 ScreenBufferInfo
.CursorSize
= ConsoleInfo
->CursorSize
;
270 InitializeListHead(&Console
->BufferList
);
271 Status
= ConDrvCreateScreenBuffer(&NewBuffer
,
273 CONSOLE_TEXTMODE_BUFFER
,
275 if (!NT_SUCCESS(Status
))
277 DPRINT1("ConDrvCreateScreenBuffer: failed, Status = 0x%08lx\n", Status
);
278 CloseHandle(Console
->InputBuffer
.ActiveEvent
);
279 DeleteCriticalSection(&Console
->Lock
);
280 ConsoleFreeHeap(Console
);
283 /* Make the new screen buffer active */
284 Console
->ActiveBuffer
= NewBuffer
;
285 Console
->UnpauseEvent
= NULL
;
287 /* Initialize the console title */
288 ConsoleCreateUnicodeString(&Console
->OriginalTitle
, ConsoleInfo
->ConsoleTitle
);
290 if (ConsoleInfo
.ConsoleTitle
[0] == L
'\0')
292 if (LoadStringW(ConSrvDllInstance
, IDS_CONSOLE_TITLE
, DefaultTitle
, sizeof(DefaultTitle
) / sizeof(DefaultTitle
[0])))
294 ConsoleCreateUnicodeString(&Console
->Title
, DefaultTitle
);
298 ConsoleCreateUnicodeString(&Console
->Title
, L
"ReactOS Console");
304 ConsoleCreateUnicodeString(&Console
->Title
, ConsoleInfo
->ConsoleTitle
);
309 DPRINT("Console initialized\n");
311 /* All went right, so add the console to the list */
312 Status
= InsertConsole(Console
);
313 if (!NT_SUCCESS(Status
))
316 ConDrvDeleteConsole(Console
);
320 /* The initialization is finished */
321 DPRINT("Change state\n");
322 Console
->State
= CONSOLE_RUNNING
;
324 /* Return the newly created console to the caller and a success code too */
325 *NewConsole
= Console
;
326 return STATUS_SUCCESS
;
330 ConDrvRegisterTerminal(IN PCONSOLE Console
,
331 IN PTERMINAL Terminal
)
335 if (Console
== NULL
|| Terminal
== NULL
)
336 return STATUS_INVALID_PARAMETER
;
338 /* FIXME: Lock the console before ?? */
341 * Attach the terminal to the console. Use now the TermIFace of the console,
342 * and not the user-defined temporary Terminal pointer.
344 Console
->TermIFace
= *Terminal
;
345 Console
->TermIFace
.Console
= Console
;
347 /* Initialize the terminal AFTER having attached it to the console */
348 DPRINT("Finish initialization of terminal\n");
349 Status
= Console
->TermIFace
.Vtbl
->InitTerminal(&Console
->TermIFace
, Console
);
350 if (!NT_SUCCESS(Status
))
352 DPRINT1("Terminal initialization failed, Status = 0x%08lx\n", Status
);
354 /* We failed, detach the terminal from the console */
355 Terminal
->Console
= NULL
; // For the caller
356 ResetTerminal(Console
);
361 /* Copy buffer contents to screen */
363 // ConioDrawConsole(Console);
364 DPRINT("Console drawn\n");
366 DPRINT("Terminal initialization done\n");
367 return STATUS_SUCCESS
;
371 ConDrvDeregisterTerminal(IN PCONSOLE Console
)
373 if (Console
== NULL
) return STATUS_INVALID_PARAMETER
;
375 /* FIXME: Lock the console before ?? */
377 /* Deinitialize the terminal BEFORE detaching it from the console */
378 Console
->TermIFace
.Vtbl
->DeinitTerminal(&Console
->TermIFace
/*, Console*/);
381 * Detach the terminal from the console:
382 * reinitialize the terminal interface.
384 ResetTerminal(Console
);
386 DPRINT("Terminal unregistered\n");
387 return STATUS_SUCCESS
;
391 ConDrvDeleteConsole(IN PCONSOLE Console
)
393 DPRINT("ConDrvDeleteConsole(0x%p)\n", Console
);
396 * Forbid validation of any console by other threads
397 * during the deletion of this console.
399 ConDrvLockConsoleListExclusive();
402 * If the console is already being destroyed, i.e. not running
403 * or finishing to be initialized, just return.
405 if (!ConDrvValidateConsoleUnsafe(Console
, CONSOLE_RUNNING
, TRUE
) &&
406 !ConDrvValidateConsoleUnsafe(Console
, CONSOLE_INITIALIZING
, TRUE
))
408 /* Unlock the console list and return */
409 ConDrvUnlockConsoleList();
414 * We are about to be destroyed. Signal it to other people
415 * so that they can terminate what they are doing, and that
416 * they cannot longer validate the console.
418 Console
->State
= CONSOLE_TERMINATING
;
421 * Allow other threads to finish their job: basically, unlock
422 * all other calls to EnterCriticalSection(&Console->Lock); by
423 * ConDrvValidateConsoleUnsafe functions so that they just see
424 * that we are not in CONSOLE_RUNNING state anymore, or unlock
425 * other concurrent calls to ConDrvDeleteConsole so that they
426 * can see that we are in fact already deleting the console.
428 LeaveCriticalSection(&Console
->Lock
);
429 ConDrvUnlockConsoleList();
431 /* FIXME: Send a terminate message to all the processes owning this console */
433 /* Cleanup the UI-oriented part */
434 DPRINT("Deregister console\n");
435 ConDrvDeregisterTerminal(Console
);
436 DPRINT("Console deregistered\n");
439 * Check that the console is in terminating state before continuing
440 * (the cleanup code must not change the state of the console...
441 * ...unless to cancel console deletion ?).
444 ConDrvLockConsoleListExclusive();
446 if (!ConDrvValidateConsoleUnsafe(Console
, CONSOLE_TERMINATING
, TRUE
))
448 ConDrvUnlockConsoleList();
452 /* We are now in destruction */
453 Console
->State
= CONSOLE_IN_DESTRUCTION
;
455 /* We really delete the console. Reset the count to be sure. */
456 Console
->ReferenceCount
= 0;
458 /* Remove the console from the list */
459 RemoveConsole(Console
);
461 /* Discard all entries in the input event queue */
462 PurgeInputBuffer(Console
);
463 if (Console
->LineBuffer
) ConsoleFreeHeap(Console
->LineBuffer
);
465 /* Delete the last screen buffer */
466 ConioDeleteScreenBuffer(Console
->ActiveBuffer
);
467 Console
->ActiveBuffer
= NULL
;
468 if (!IsListEmpty(&Console
->BufferList
))
470 DPRINT1("BUG: screen buffer list not empty\n");
474 /**/ CloseHandle(Console
->InputBuffer
.ActiveEvent
); /**/
475 if (Console
->UnpauseEvent
) CloseHandle(Console
->UnpauseEvent
);
477 ConsoleFreeUnicodeString(&Console
->OriginalTitle
);
478 ConsoleFreeUnicodeString(&Console
->Title
);
480 DPRINT("ConDrvDeleteConsole - Unlocking\n");
481 LeaveCriticalSection(&Console
->Lock
);
482 DPRINT("ConDrvDeleteConsole - Destroying lock\n");
483 DeleteCriticalSection(&Console
->Lock
);
484 DPRINT("ConDrvDeleteConsole - Lock destroyed ; freeing console\n");
486 ConsoleFreeHeap(Console
);
487 DPRINT("ConDrvDeleteConsole - Console destroyed\n");
489 /* Unlock the console list and return */
490 ConDrvUnlockConsoleList();
494 /* PUBLIC DRIVER APIS *********************************************************/
497 ConDrvGetConsoleMode(IN PCONSOLE Console
,
498 IN PCONSOLE_IO_OBJECT Object
,
499 OUT PULONG ConsoleMode
)
501 NTSTATUS Status
= STATUS_SUCCESS
;
503 if (Console
== NULL
|| Object
== NULL
|| ConsoleMode
== NULL
)
504 return STATUS_INVALID_PARAMETER
;
507 ASSERT(Console
== Object
->Console
);
509 /*** FIXME: */ *ConsoleMode
= 0; /***/
511 if (INPUT_BUFFER
== Object
->Type
)
513 PCONSOLE_INPUT_BUFFER InputBuffer
= (PCONSOLE_INPUT_BUFFER
)Object
;
514 *ConsoleMode
= InputBuffer
->Mode
;
516 else if (TEXTMODE_BUFFER
== Object
->Type
|| GRAPHICS_BUFFER
== Object
->Type
)
518 PCONSOLE_SCREEN_BUFFER Buffer
= (PCONSOLE_SCREEN_BUFFER
)Object
;
519 *ConsoleMode
= Buffer
->Mode
;
523 Status
= STATUS_INVALID_HANDLE
;
530 ConDrvSetConsoleMode(IN PCONSOLE Console
,
531 IN PCONSOLE_IO_OBJECT Object
,
532 IN ULONG ConsoleMode
)
534 #define CONSOLE_VALID_INPUT_MODES ( ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | \
535 ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT | \
537 #define CONSOLE_VALID_OUTPUT_MODES ( ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT )
539 NTSTATUS Status
= STATUS_SUCCESS
;
541 if (Console
== NULL
|| Object
== NULL
)
542 return STATUS_INVALID_PARAMETER
;
545 ASSERT(Console
== Object
->Console
);
547 if (INPUT_BUFFER
== Object
->Type
)
549 PCONSOLE_INPUT_BUFFER InputBuffer
= (PCONSOLE_INPUT_BUFFER
)Object
;
551 /* Only the presence of valid mode flags is allowed */
552 if (ConsoleMode
& ~CONSOLE_VALID_INPUT_MODES
)
554 Status
= STATUS_INVALID_PARAMETER
;
558 InputBuffer
->Mode
= (ConsoleMode
& CONSOLE_VALID_INPUT_MODES
);
561 else if (TEXTMODE_BUFFER
== Object
->Type
|| GRAPHICS_BUFFER
== Object
->Type
)
563 PCONSOLE_SCREEN_BUFFER Buffer
= (PCONSOLE_SCREEN_BUFFER
)Object
;
565 /* Only the presence of valid mode flags is allowed */
566 if (ConsoleMode
& ~CONSOLE_VALID_OUTPUT_MODES
)
568 Status
= STATUS_INVALID_PARAMETER
;
572 Buffer
->Mode
= (ConsoleMode
& CONSOLE_VALID_OUTPUT_MODES
);
577 Status
= STATUS_INVALID_HANDLE
;
584 ConDrvGetConsoleTitle(IN PCONSOLE Console
,
586 IN OUT PVOID TitleBuffer
,
587 IN OUT PULONG BufLength
)
591 if (Console
== NULL
|| TitleBuffer
== NULL
|| BufLength
== NULL
)
592 return STATUS_INVALID_PARAMETER
;
594 /* Copy title of the console to the user title buffer */
597 if (*BufLength
>= sizeof(WCHAR
))
599 Length
= min(*BufLength
- sizeof(WCHAR
), Console
->Title
.Length
);
600 RtlCopyMemory(TitleBuffer
, Console
->Title
.Buffer
, Length
);
601 ((PWCHAR
)TitleBuffer
)[Length
/ sizeof(WCHAR
)] = L
'\0';
606 *BufLength
= Console
->Title
.Length
;
611 if (*BufLength
>= sizeof(CHAR
))
613 Length
= min(*BufLength
- sizeof(CHAR
), Console
->Title
.Length
/ sizeof(WCHAR
));
614 Length
= WideCharToMultiByte(Console
->InputCodePage
, 0,
615 Console
->Title
.Buffer
, Length
,
618 ((PCHAR
)TitleBuffer
)[Length
] = '\0';
623 *BufLength
= Console
->Title
.Length
/ sizeof(WCHAR
);
627 return STATUS_SUCCESS
;
631 ConDrvSetConsoleTitle(IN PCONSOLE Console
,
633 IN PVOID TitleBuffer
,
639 if (Console
== NULL
|| TitleBuffer
== NULL
)
640 return STATUS_INVALID_PARAMETER
;
644 /* Length is in bytes */
649 /* Use the console input CP for the conversion */
650 Length
= MultiByteToWideChar(Console
->InputCodePage
, 0,
651 TitleBuffer
, BufLength
,
653 /* The returned Length was in number of wchars, convert it in bytes */
654 Length
*= sizeof(WCHAR
);
657 /* Allocate a new buffer to hold the new title (NULL-terminated) */
658 Buffer
= ConsoleAllocHeap(HEAP_ZERO_MEMORY
, Length
+ sizeof(WCHAR
));
659 if (!Buffer
) return STATUS_NO_MEMORY
;
661 /* Free the old title */
662 ConsoleFreeUnicodeString(&Console
->Title
);
664 /* Copy title to console */
665 Console
->Title
.Buffer
= Buffer
;
666 Console
->Title
.Length
= Length
;
667 Console
->Title
.MaximumLength
= Console
->Title
.Length
+ sizeof(WCHAR
);
671 RtlCopyMemory(Console
->Title
.Buffer
, TitleBuffer
, Console
->Title
.Length
);
675 MultiByteToWideChar(Console
->InputCodePage
, 0,
676 TitleBuffer
, BufLength
,
677 Console
->Title
.Buffer
,
678 Console
->Title
.Length
/ sizeof(WCHAR
));
682 Console
->Title
.Buffer
[Console
->Title
.Length
/ sizeof(WCHAR
)] = L
'\0';
684 // TermChangeTitle(Console);
685 return STATUS_SUCCESS
;
689 ConDrvGetConsoleCP(IN PCONSOLE Console
,
693 if (Console
== NULL
|| CodePage
== NULL
)
694 return STATUS_INVALID_PARAMETER
;
696 *CodePage
= (OutputCP
? Console
->OutputCodePage
: Console
->InputCodePage
);
698 return STATUS_SUCCESS
;
702 ConDrvSetConsoleCP(IN PCONSOLE Console
,
706 if (Console
== NULL
|| !IsValidCodePage(CodePage
))
707 return STATUS_INVALID_PARAMETER
;
710 Console
->OutputCodePage
= CodePage
;
712 Console
->InputCodePage
= CodePage
;
714 return STATUS_SUCCESS
;