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 // FIXME: Add this prototype to winternl.h / rtlfuncs.h / ...
22 NTSTATUS NTAPI
RtlGetLastNtStatus(VOID
);
25 /* GLOBALS ********************************************************************/
27 static ULONG ConsoleListSize
;
28 static PCONSOLE
* ConsoleList
; /* The list of all the allocated consoles */
29 static RTL_RESOURCE ListLock
;
31 #define ConDrvLockConsoleListExclusive() \
32 RtlAcquireResourceExclusive(&ListLock, TRUE)
34 #define ConDrvLockConsoleListShared() \
35 RtlAcquireResourceShared(&ListLock, TRUE)
37 #define ConDrvUnlockConsoleList() \
38 RtlReleaseResource(&ListLock)
40 // Adapted from reactos/lib/rtl/unicode.c, RtlCreateUnicodeString line 2180
42 ConsoleCreateUnicodeString(IN OUT PUNICODE_STRING UniDest
,
45 SIZE_T Size
= (wcslen(Source
) + 1) * sizeof(WCHAR
);
46 if (Size
> MAXUSHORT
) return FALSE
;
48 UniDest
->Buffer
= ConsoleAllocHeap(HEAP_ZERO_MEMORY
, Size
);
49 if (UniDest
->Buffer
== NULL
) return FALSE
;
51 RtlCopyMemory(UniDest
->Buffer
, Source
, Size
);
52 UniDest
->MaximumLength
= (USHORT
)Size
;
53 UniDest
->Length
= (USHORT
)Size
- sizeof(WCHAR
);
58 // Adapted from reactos/lib/rtl/unicode.c, RtlFreeUnicodeString line 431
60 ConsoleFreeUnicodeString(IN PUNICODE_STRING UnicodeString
)
62 if (UnicodeString
->Buffer
)
64 ConsoleFreeHeap(UnicodeString
->Buffer
);
65 RtlZeroMemory(UnicodeString
, sizeof(UNICODE_STRING
));
71 InsertConsole(OUT PHANDLE Handle
,
74 #define CONSOLE_HANDLES_INCREMENT 2 * 3
76 NTSTATUS Status
= STATUS_SUCCESS
;
80 ASSERT( (ConsoleList
== NULL
&& ConsoleListSize
== 0) ||
81 (ConsoleList
!= NULL
&& ConsoleListSize
!= 0) );
83 /* All went right, so add the console to the list */
84 ConDrvLockConsoleListExclusive();
85 DPRINT1("Insert in the list\n");
89 for (i
= 0; i
< ConsoleListSize
; i
++)
91 if (ConsoleList
[i
] == NULL
) break;
95 if (i
>= ConsoleListSize
)
97 DPRINT1("Creation of a new handles table\n");
98 /* Allocate a new handles table */
99 Block
= ConsoleAllocHeap(HEAP_ZERO_MEMORY
,
101 CONSOLE_HANDLES_INCREMENT
) * sizeof(PCONSOLE
));
104 Status
= STATUS_UNSUCCESSFUL
;
108 /* If we previously had a handles table, free it and use the new one */
111 /* Copy the handles from the old table to the new one */
114 ConsoleListSize
* sizeof(PCONSOLE
));
115 ConsoleFreeHeap(ConsoleList
);
118 ConsoleListSize
+= CONSOLE_HANDLES_INCREMENT
;
121 ConsoleList
[i
] = Console
;
122 *Handle
= ULongToHandle((i
<< 2) | 0x3);
125 /* Unlock the console list and return status */
126 ConDrvUnlockConsoleList();
133 RemoveConsoleByHandle(IN HANDLE Handle
)
135 NTSTATUS Status
= STATUS_SUCCESS
;
138 BOOLEAN ValidHandle
= ((HandleToULong(Handle
) & 0x3) == 0x3);
139 ULONG Index
= HandleToULong(Handle
) >> 2;
141 if (!ValidHandle
) return STATUS_INVALID_HANDLE
;
143 ASSERT( (ConsoleList
== NULL
&& ConsoleListSize
== 0) ||
144 (ConsoleList
!= NULL
&& ConsoleListSize
!= 0) );
146 /* Remove the console from the list */
147 ConDrvLockConsoleListExclusive();
149 if (Index
>= ConsoleListSize
||
150 (Console
= ConsoleList
[Index
]) == NULL
)
152 Status
= STATUS_INVALID_HANDLE
;
156 ConsoleList
[Index
] = NULL
;
159 /* Unlock the console list and return status */
160 ConDrvUnlockConsoleList();
166 RemoveConsoleByPointer(IN PCONSOLE Console
)
170 if (!Console
) return STATUS_INVALID_PARAMETER
;
172 ASSERT( (ConsoleList
== NULL
&& ConsoleListSize
== 0) ||
173 (ConsoleList
!= NULL
&& ConsoleListSize
!= 0) );
175 /* Remove the console from the list */
176 ConDrvLockConsoleListExclusive();
180 for (i
= 0; i
< ConsoleListSize
; i
++)
182 if (ConsoleList
[i
] == Console
) ConsoleList
[i
] = NULL
;
186 /* Unlock the console list */
187 ConDrvUnlockConsoleList();
189 return STATUS_SUCCESS
;
193 /* For resetting the frontend - defined in dummyfrontend.c */
194 VOID
ResetFrontEnd(IN PCONSOLE Console
);
197 /* PRIVATE FUNCTIONS **********************************************************/
200 ConDrvConsoleCtrlEventTimeout(IN ULONG CtrlEvent
,
201 IN PCONSOLE_PROCESS_DATA ProcessData
,
204 NTSTATUS Status
= STATUS_SUCCESS
;
206 DPRINT("ConDrvConsoleCtrlEventTimeout Parent ProcessId = %x\n", ProcessData
->Process
->ClientId
.UniqueProcess
);
208 if (ProcessData
->CtrlDispatcher
)
212 HANDLE Thread
= NULL
;
216 Thread
= CreateRemoteThread(ProcessData
->Process
->ProcessHandle
, NULL
, 0,
217 ProcessData
->CtrlDispatcher
,
218 UlongToPtr(CtrlEvent
), 0, NULL
);
221 Status
= RtlGetLastNtStatus();
222 DPRINT1("Failed thread creation, Status = 0x%08lx\n", Status
);
226 DPRINT("ProcessData->CtrlDispatcher remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n", ProcessData
->Process
->ClientId
.UniqueProcess
, ProcessData
->Process
);
227 WaitForSingleObject(Thread
, Timeout
);
236 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
238 Status
= _SEH2_GetExceptionCode();
239 DPRINT1("ConDrvConsoleCtrlEventTimeout - Caught an exception, Status = 0x%08lx\n", Status
);
248 ConDrvConsoleCtrlEvent(IN ULONG CtrlEvent
,
249 IN PCONSOLE_PROCESS_DATA ProcessData
)
251 return ConDrvConsoleCtrlEventTimeout(CtrlEvent
, ProcessData
, 0);
255 ConioPause(PCONSOLE Console
, UINT Flags
)
257 Console
->PauseFlags
|= Flags
;
258 if (!Console
->UnpauseEvent
)
259 Console
->UnpauseEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
263 ConioUnpause(PCONSOLE Console
, UINT Flags
)
265 Console
->PauseFlags
&= ~Flags
;
267 // if ((Console->PauseFlags & (PAUSED_FROM_KEYBOARD | PAUSED_FROM_SCROLLBAR | PAUSED_FROM_SELECTION)) == 0)
268 if (Console
->PauseFlags
== 0 && Console
->UnpauseEvent
)
270 SetEvent(Console
->UnpauseEvent
);
271 CloseHandle(Console
->UnpauseEvent
);
272 Console
->UnpauseEvent
= NULL
;
274 CsrNotifyWait(&Console
->WriteWaitQueue
,
278 if (!IsListEmpty(&Console
->WriteWaitQueue
))
280 CsrDereferenceWait(&Console
->WriteWaitQueue
);
287 * Console accessibility check helpers
291 ConDrvValidateConsoleState(IN PCONSOLE Console
,
292 IN CONSOLE_STATE ExpectedState
)
294 // if (!Console) return FALSE;
296 /* The console must be locked */
297 // ASSERT(Console_locked);
299 return (Console
->State
== ExpectedState
);
303 ConDrvValidateConsoleUnsafe(IN PCONSOLE Console
,
304 IN CONSOLE_STATE ExpectedState
,
305 IN BOOLEAN LockConsole
)
307 if (!Console
) return FALSE
;
310 * Lock the console to forbid possible console's state changes
311 * (which must be done when the console is already locked).
312 * If we don't want to lock it, it's because the lock is already
313 * held. So there must be no problems.
315 if (LockConsole
) EnterCriticalSection(&Console
->Lock
);
317 // ASSERT(Console_locked);
319 /* Check whether the console's state is what we expect */
320 if (!ConDrvValidateConsoleState(Console
, ExpectedState
))
322 if (LockConsole
) LeaveCriticalSection(&Console
->Lock
);
330 ConDrvValidateConsole(OUT PCONSOLE
* Console
,
331 IN HANDLE ConsoleHandle
,
332 IN CONSOLE_STATE ExpectedState
,
333 IN BOOLEAN LockConsole
)
335 BOOLEAN RetVal
= FALSE
;
336 PCONSOLE ValidatedConsole
;
338 BOOLEAN ValidHandle
= ((HandleToULong(ConsoleHandle
) & 0x3) == 0x3);
339 ULONG Index
= HandleToULong(ConsoleHandle
) >> 2;
341 if (!ValidHandle
) return FALSE
;
343 if (!Console
) return FALSE
;
347 * Forbid creation or deletion of consoles when
348 * checking for the existence of a console.
350 ConDrvLockConsoleListShared();
352 if (Index
>= ConsoleListSize
||
353 (ValidatedConsole
= ConsoleList
[Index
]) == NULL
)
355 /* Unlock the console list */
356 ConDrvUnlockConsoleList();
361 ValidatedConsole
= ConsoleList
[Index
];
363 /* Unlock the console list and return */
364 ConDrvUnlockConsoleList();
366 RetVal
= ConDrvValidateConsoleUnsafe(ValidatedConsole
,
369 if (RetVal
) *Console
= ValidatedConsole
;
375 ConDrvGetConsole(OUT PCONSOLE
* Console
,
376 IN HANDLE ConsoleHandle
,
377 IN BOOLEAN LockConsole
)
379 NTSTATUS Status
= STATUS_INVALID_HANDLE
;
380 PCONSOLE GrabConsole
;
382 if (Console
== NULL
) return STATUS_INVALID_PARAMETER
;
385 if (ConDrvValidateConsole(&GrabConsole
,
390 InterlockedIncrement(&GrabConsole
->ReferenceCount
);
391 *Console
= GrabConsole
;
392 Status
= STATUS_SUCCESS
;
399 ConDrvReleaseConsole(IN PCONSOLE Console
,
400 IN BOOLEAN WasConsoleLocked
)
404 if (!Console
) return;
405 // if (Console->ReferenceCount == 0) return; // This shouldn't happen
406 ASSERT(Console
->ReferenceCount
> 0);
408 /* The console must be locked */
409 // ASSERT(Console_locked);
412 * Decrement the reference count. Save the new value too,
413 * because Console->ReferenceCount might be modified after
414 * the console gets unlocked but before we check whether we
417 RefCount
= _InterlockedDecrement(&Console
->ReferenceCount
);
419 /* Unlock the console if needed */
420 if (WasConsoleLocked
) LeaveCriticalSection(&Console
->Lock
);
422 /* Delete the console if needed */
423 if (RefCount
<= 0) ConDrvDeleteConsole(Console
);
427 /* CONSOLE INITIALIZATION FUNCTIONS *******************************************/
430 ConDrvInitConsoleSupport(VOID
)
432 DPRINT("CONSRV: ConDrvInitConsoleSupport()\n");
434 /* Initialize the console list and its lock */
437 RtlInitializeResource(&ListLock
);
439 /* Should call LoadKeyboardLayout */
443 ConDrvInitConsole(OUT PHANDLE NewConsoleHandle
,
444 OUT PCONSOLE
* NewConsole
,
445 IN PCONSOLE_INFO ConsoleInfo
,
446 IN ULONG ConsoleLeaderProcessId
)
449 SECURITY_ATTRIBUTES SecurityAttributes
;
450 // CONSOLE_INFO CapturedConsoleInfo;
451 TEXTMODE_BUFFER_INFO ScreenBufferInfo
;
452 HANDLE ConsoleHandle
;
454 PCONSOLE_SCREEN_BUFFER NewBuffer
;
455 // WCHAR DefaultTitle[128];
457 if (NewConsoleHandle
== NULL
|| NewConsole
== NULL
|| ConsoleInfo
== NULL
)
458 return STATUS_INVALID_PARAMETER
;
460 *NewConsoleHandle
= NULL
;
464 * Allocate a console structure
466 Console
= ConsoleAllocHeap(HEAP_ZERO_MEMORY
, sizeof(CONSOLE
));
469 DPRINT1("Not enough memory for console creation.\n");
470 return STATUS_NO_MEMORY
;
474 * Load the console settings
477 /* 1. Load the default settings */
478 // ConSrvGetDefaultSettings(ConsoleInfo, ProcessId);
480 // /* 2. Get the title of the console (initialize ConsoleInfo.ConsoleTitle) */
481 // Length = min(wcslen(ConsoleStartInfo->ConsoleTitle),
482 // sizeof(ConsoleInfo.ConsoleTitle) / sizeof(ConsoleInfo.ConsoleTitle[0]) - 1);
483 // wcsncpy(ConsoleInfo.ConsoleTitle, ConsoleStartInfo->ConsoleTitle, Length);
484 // ConsoleInfo.ConsoleTitle[Length] = L'\0';
487 * 4. Load the remaining console settings via the registry.
490 if ((ConsoleStartInfo
->dwStartupFlags
& STARTF_TITLEISLINKNAME
) == 0)
493 * Either we weren't created by an app launched via a shell-link,
494 * or we failed to load shell-link console properties.
495 * Therefore, load the console infos for the application from the registry.
497 ConSrvReadUserSettings(ConsoleInfo
, ProcessId
);
500 * Now, update them with the properties the user might gave to us
501 * via the STARTUPINFO structure before calling CreateProcess
502 * (and which was transmitted via the ConsoleStartInfo structure).
503 * We therefore overwrite the values read in the registry.
505 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_USEFILLATTRIBUTE
)
507 ConsoleInfo
->ScreenAttrib
= (USHORT
)ConsoleStartInfo
->FillAttribute
;
509 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_USECOUNTCHARS
)
511 ConsoleInfo
->ScreenBufferSize
= ConsoleStartInfo
->ScreenBufferSize
;
513 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_USESIZE
)
515 // ConsoleInfo->ConsoleSize = ConsoleStartInfo->ConsoleWindowSize;
516 ConsoleInfo
->ConsoleSize
.X
= (SHORT
)ConsoleStartInfo
->ConsoleWindowSize
.cx
;
517 ConsoleInfo
->ConsoleSize
.Y
= (SHORT
)ConsoleStartInfo
->ConsoleWindowSize
.cy
;
523 * Fix the screen buffer size if needed. The rule is:
524 * ScreenBufferSize >= ConsoleSize
526 if (ConsoleInfo
->ScreenBufferSize
.X
< ConsoleInfo
->ConsoleSize
.X
)
527 ConsoleInfo
->ScreenBufferSize
.X
= ConsoleInfo
->ConsoleSize
.X
;
528 if (ConsoleInfo
->ScreenBufferSize
.Y
< ConsoleInfo
->ConsoleSize
.Y
)
529 ConsoleInfo
->ScreenBufferSize
.Y
= ConsoleInfo
->ConsoleSize
.Y
;
532 * Initialize the console
534 Console
->State
= CONSOLE_INITIALIZING
;
535 Console
->ReferenceCount
= 0;
536 InitializeCriticalSection(&Console
->Lock
);
537 InitializeListHead(&Console
->ProcessList
);
538 Console
->NotifiedLastCloseProcess
= NULL
;
539 Console
->NotifyLastClose
= FALSE
;
541 /* Initialize the frontend interface */
542 ResetFrontEnd(Console
);
544 memcpy(Console
->Colors
, ConsoleInfo
->Colors
, sizeof(ConsoleInfo
->Colors
));
545 Console
->ConsoleSize
= ConsoleInfo
->ConsoleSize
;
546 Console
->FixedSize
= FALSE
; // Value by default; is reseted by the front-ends if needed.
549 * Initialize the input buffer
551 ConSrvInitObject(&Console
->InputBuffer
.Header
, INPUT_BUFFER
, Console
);
553 SecurityAttributes
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
554 SecurityAttributes
.lpSecurityDescriptor
= NULL
;
555 SecurityAttributes
.bInheritHandle
= TRUE
;
556 Console
->InputBuffer
.ActiveEvent
= CreateEventW(&SecurityAttributes
, TRUE
, FALSE
, NULL
);
557 if (NULL
== Console
->InputBuffer
.ActiveEvent
)
559 DeleteCriticalSection(&Console
->Lock
);
560 ConsoleFreeHeap(Console
);
561 return STATUS_UNSUCCESSFUL
;
564 Console
->InputBuffer
.InputBufferSize
= 0; // FIXME!
565 InitializeListHead(&Console
->InputBuffer
.InputEvents
);
566 InitializeListHead(&Console
->InputBuffer
.ReadWaitQueue
);
567 Console
->InputBuffer
.Mode
= ENABLE_PROCESSED_INPUT
| ENABLE_LINE_INPUT
|
568 ENABLE_ECHO_INPUT
| ENABLE_MOUSE_INPUT
;
570 Console
->QuickEdit
= ConsoleInfo
->QuickEdit
;
571 Console
->InsertMode
= ConsoleInfo
->InsertMode
;
572 Console
->LineBuffer
= NULL
;
573 Console
->LineMaxSize
= Console
->LineSize
= Console
->LinePos
= 0;
574 Console
->LineComplete
= Console
->LineUpPressed
= FALSE
;
575 Console
->LineInsertToggle
= Console
->InsertMode
;
578 /* Set-up the code page */
579 Console
->CodePage
= Console
->OutputCodePage
= ConsoleInfo
->CodePage
;
581 /* Initialize a new text-mode screen buffer with default settings */
582 ScreenBufferInfo
.ScreenBufferSize
= ConsoleInfo
->ScreenBufferSize
;
583 ScreenBufferInfo
.ScreenAttrib
= ConsoleInfo
->ScreenAttrib
;
584 ScreenBufferInfo
.PopupAttrib
= ConsoleInfo
->PopupAttrib
;
585 ScreenBufferInfo
.IsCursorVisible
= TRUE
;
586 ScreenBufferInfo
.CursorSize
= ConsoleInfo
->CursorSize
;
588 InitializeListHead(&Console
->BufferList
);
589 Status
= ConDrvCreateScreenBuffer(&NewBuffer
,
591 CONSOLE_TEXTMODE_BUFFER
,
593 if (!NT_SUCCESS(Status
))
595 DPRINT1("ConDrvCreateScreenBuffer: failed, Status = 0x%08lx\n", Status
);
596 CloseHandle(Console
->InputBuffer
.ActiveEvent
);
597 DeleteCriticalSection(&Console
->Lock
);
598 ConsoleFreeHeap(Console
);
601 /* Make the new screen buffer active */
602 Console
->ActiveBuffer
= NewBuffer
;
603 InitializeListHead(&Console
->WriteWaitQueue
);
604 Console
->PauseFlags
= 0;
605 Console
->UnpauseEvent
= NULL
;
608 * Initialize the alias and history buffers
610 Console
->Aliases
= NULL
;
611 InitializeListHead(&Console
->HistoryBuffers
);
612 Console
->HistoryBufferSize
= ConsoleInfo
->HistoryBufferSize
;
613 Console
->NumberOfHistoryBuffers
= ConsoleInfo
->NumberOfHistoryBuffers
;
614 Console
->HistoryNoDup
= ConsoleInfo
->HistoryNoDup
;
616 /* Initialize the console title */
617 ConsoleCreateUnicodeString(&Console
->OriginalTitle
, ConsoleInfo
->ConsoleTitle
);
618 // if (ConsoleInfo.ConsoleTitle[0] == L'\0')
620 // if (LoadStringW(ConSrvDllInstance, IDS_CONSOLE_TITLE, DefaultTitle, sizeof(DefaultTitle) / sizeof(DefaultTitle[0])))
622 // ConsoleCreateUnicodeString(&Console->Title, DefaultTitle);
626 // ConsoleCreateUnicodeString(&Console->Title, L"ReactOS Console");
631 ConsoleCreateUnicodeString(&Console
->Title
, ConsoleInfo
->ConsoleTitle
);
634 /* Lock the console until its initialization is finished */
635 // EnterCriticalSection(&Console->Lock);
637 DPRINT("Console initialized\n");
639 /* All went right, so add the console to the list */
640 Status
= InsertConsole(&ConsoleHandle
, Console
);
641 if (!NT_SUCCESS(Status
))
644 ConDrvDeleteConsole(Console
);
648 /* The initialization is finished */
649 DPRINT("Change state\n");
650 Console
->State
= CONSOLE_RUNNING
;
652 /* Unlock the console */
653 // LeaveCriticalSection(&Console->Lock);
655 /* Return the newly created console to the caller and a success code too */
656 *NewConsoleHandle
= ConsoleHandle
;
657 *NewConsole
= Console
;
658 return STATUS_SUCCESS
;
662 ConDrvRegisterFrontEnd(IN PCONSOLE Console
,
663 IN PFRONTEND FrontEnd
)
667 if (Console
== NULL
|| FrontEnd
== NULL
)
668 return STATUS_INVALID_PARAMETER
;
670 /* FIXME: Lock the console before ?? */
673 * Attach the frontend to the console. Use now the TermIFace of the console,
674 * and not the user-defined temporary FrontEnd pointer.
676 Console
->TermIFace
= *FrontEnd
;
677 Console
->TermIFace
.Console
= Console
;
679 /* Initialize the frontend AFTER having attached it to the console */
680 DPRINT("Finish initialization of frontend\n");
681 Status
= Console
->TermIFace
.Vtbl
->InitFrontEnd(&Console
->TermIFace
, Console
);
682 if (!NT_SUCCESS(Status
))
684 DPRINT1("FrontEnd initialization failed, Status = 0x%08lx\n", Status
);
686 /* We failed, detach the frontend from the console */
687 FrontEnd
->Console
= NULL
; // For the caller
688 ResetFrontEnd(Console
);
693 /* Copy buffer contents to screen */
695 // ConioDrawConsole(Console);
696 DPRINT("Console drawn\n");
698 DPRINT("Terminal FrontEnd initialization done\n");
699 return STATUS_SUCCESS
;
703 ConDrvDeregisterFrontEnd(IN PCONSOLE Console
)
705 if (Console
== NULL
) return STATUS_INVALID_PARAMETER
;
707 /* FIXME: Lock the console before ?? */
709 /* Deinitialize the frontend BEFORE detaching it from the console */
710 Console
->TermIFace
.Vtbl
->DeinitFrontEnd(&Console
->TermIFace
/*, Console*/);
713 * Detach the frontend from the console:
714 * reinitialize the frontend interface.
716 ResetFrontEnd(Console
);
718 DPRINT("Terminal FrontEnd unregistered\n");
719 return STATUS_SUCCESS
;
723 ConDrvDeleteConsole(IN PCONSOLE Console
)
725 DPRINT("ConDrvDeleteConsole(0x%p)\n", Console
);
728 * Forbid validation of any console by other threads
729 * during the deletion of this console.
731 ConDrvLockConsoleListExclusive();
734 * If the console is already being destroyed, i.e. not running
735 * or finishing to be initialized, just return.
737 if (!ConDrvValidateConsoleUnsafe(Console
, CONSOLE_RUNNING
, TRUE
) &&
738 !ConDrvValidateConsoleUnsafe(Console
, CONSOLE_INITIALIZING
, TRUE
))
740 /* Unlock the console list and return */
741 ConDrvUnlockConsoleList();
746 * We are about to be destroyed. Signal it to other people
747 * so that they can terminate what they are doing, and that
748 * they cannot longer validate the console.
750 Console
->State
= CONSOLE_TERMINATING
;
753 * Allow other threads to finish their job: basically, unlock
754 * all other calls to EnterCriticalSection(&Console->Lock); by
755 * ConDrvValidateConsole(Unsafe) functions so that they just see
756 * that we are not in CONSOLE_RUNNING state anymore, or unlock
757 * other concurrent calls to ConDrvDeleteConsole so that they
758 * can see that we are in fact already deleting the console.
760 LeaveCriticalSection(&Console
->Lock
);
761 ConDrvUnlockConsoleList();
763 /* FIXME: Send a terminate message to all the processes owning this console */
765 /* Cleanup the UI-oriented part */
766 DPRINT("Deregister console\n");
767 ConDrvDeregisterFrontEnd(Console
);
768 DPRINT("Console deregistered\n");
771 * Check that the console is in terminating state before continuing
772 * (the cleanup code must not change the state of the console...
773 * ...unless to cancel console deletion ?).
776 ConDrvLockConsoleListExclusive();
778 if (!ConDrvValidateConsoleUnsafe(Console
, CONSOLE_TERMINATING
, TRUE
))
780 ConDrvUnlockConsoleList();
784 /* We are now in destruction */
785 Console
->State
= CONSOLE_IN_DESTRUCTION
;
787 /* We really delete the console. Reset the count to be sure. */
788 Console
->ReferenceCount
= 0;
790 /* Remove the console from the list */
791 RemoveConsoleByPointer(Console
);
793 /* Discard all entries in the input event queue */
794 PurgeInputBuffer(Console
);
796 if (Console
->LineBuffer
) ConsoleFreeHeap(Console
->LineBuffer
);
798 IntDeleteAllAliases(Console
);
799 HistoryDeleteBuffers(Console
);
801 ConioDeleteScreenBuffer(Console
->ActiveBuffer
);
802 Console
->ActiveBuffer
= NULL
;
803 if (!IsListEmpty(&Console
->BufferList
))
805 DPRINT1("BUG: screen buffer list not empty\n");
809 /**/ CloseHandle(Console
->InputBuffer
.ActiveEvent
); /**/
810 if (Console
->UnpauseEvent
) CloseHandle(Console
->UnpauseEvent
);
812 ConsoleFreeUnicodeString(&Console
->OriginalTitle
);
813 ConsoleFreeUnicodeString(&Console
->Title
);
815 DPRINT("ConDrvDeleteConsole - Unlocking\n");
816 LeaveCriticalSection(&Console
->Lock
);
817 DPRINT("ConDrvDeleteConsole - Destroying lock\n");
818 DeleteCriticalSection(&Console
->Lock
);
819 DPRINT("ConDrvDeleteConsole - Lock destroyed ; freeing console\n");
821 ConsoleFreeHeap(Console
);
822 DPRINT("ConDrvDeleteConsole - Console destroyed\n");
824 /* Unlock the console list and return */
825 ConDrvUnlockConsoleList();
829 /* PUBLIC DRIVER APIS *********************************************************/
832 ConDrvGetConsoleMode(IN PCONSOLE Console
,
833 IN PCONSOLE_IO_OBJECT Object
,
834 OUT PULONG ConsoleMode
)
836 NTSTATUS Status
= STATUS_SUCCESS
;
838 if (Console
== NULL
|| Object
== NULL
|| ConsoleMode
== NULL
)
839 return STATUS_INVALID_PARAMETER
;
842 ASSERT(Console
== Object
->Console
);
844 /*** FIXME: */ *ConsoleMode
= 0; /***/
846 if (INPUT_BUFFER
== Object
->Type
)
848 PCONSOLE_INPUT_BUFFER InputBuffer
= (PCONSOLE_INPUT_BUFFER
)Object
;
850 *ConsoleMode
= InputBuffer
->Mode
;
852 if (Console
->QuickEdit
|| Console
->InsertMode
)
854 // Windows does this, even if it's not documented on MSDN
855 *ConsoleMode
|= ENABLE_EXTENDED_FLAGS
;
857 if (Console
->QuickEdit
) *ConsoleMode
|= ENABLE_QUICK_EDIT_MODE
;
858 if (Console
->InsertMode
) *ConsoleMode
|= ENABLE_INSERT_MODE
;
861 else if (TEXTMODE_BUFFER
== Object
->Type
|| GRAPHICS_BUFFER
== Object
->Type
)
863 PCONSOLE_SCREEN_BUFFER Buffer
= (PCONSOLE_SCREEN_BUFFER
)Object
;
864 *ConsoleMode
= Buffer
->Mode
;
868 Status
= STATUS_INVALID_HANDLE
;
875 ConDrvSetConsoleMode(IN PCONSOLE Console
,
876 IN PCONSOLE_IO_OBJECT Object
,
877 IN ULONG ConsoleMode
)
879 #define CONSOLE_VALID_CONTROL_MODES ( ENABLE_EXTENDED_FLAGS | \
880 ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE )
881 #define CONSOLE_VALID_INPUT_MODES ( ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | \
882 ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT | \
884 #define CONSOLE_VALID_OUTPUT_MODES ( ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT )
886 NTSTATUS Status
= STATUS_SUCCESS
;
888 if (Console
== NULL
|| Object
== NULL
)
889 return STATUS_INVALID_PARAMETER
;
892 ASSERT(Console
== Object
->Console
);
894 if (INPUT_BUFFER
== Object
->Type
)
896 PCONSOLE_INPUT_BUFFER InputBuffer
= (PCONSOLE_INPUT_BUFFER
)Object
;
898 DPRINT("SetConsoleMode(Input, %d)\n", ConsoleMode
);
901 * 1. Only the presence of valid mode flags is allowed.
903 if (ConsoleMode
& ~(CONSOLE_VALID_INPUT_MODES
| CONSOLE_VALID_CONTROL_MODES
))
905 Status
= STATUS_INVALID_PARAMETER
;
910 * 2. If we use control mode flags without ENABLE_EXTENDED_FLAGS,
911 * then consider the flags invalid.
913 if ( (ConsoleMode & CONSOLE_VALID_CONTROL_MODES) &&
914 (ConsoleMode & ENABLE_EXTENDED_FLAGS) == 0 )
916 Status = STATUS_INVALID_PARAMETER;
922 * 3. Now we can continue.
924 if (ConsoleMode
& CONSOLE_VALID_CONTROL_MODES
)
926 Console
->QuickEdit
= !!(ConsoleMode
& ENABLE_QUICK_EDIT_MODE
);
927 Console
->InsertMode
= !!(ConsoleMode
& ENABLE_INSERT_MODE
);
929 InputBuffer
->Mode
= (ConsoleMode
& CONSOLE_VALID_INPUT_MODES
);
931 else if (TEXTMODE_BUFFER
== Object
->Type
|| GRAPHICS_BUFFER
== Object
->Type
)
933 PCONSOLE_SCREEN_BUFFER Buffer
= (PCONSOLE_SCREEN_BUFFER
)Object
;
935 DPRINT("SetConsoleMode(Output, %d)\n", ConsoleMode
);
937 if (ConsoleMode
& ~CONSOLE_VALID_OUTPUT_MODES
)
939 Status
= STATUS_INVALID_PARAMETER
;
943 Buffer
->Mode
= (ConsoleMode
& CONSOLE_VALID_OUTPUT_MODES
);
948 Status
= STATUS_INVALID_HANDLE
;
956 ConDrvGetConsoleTitle(IN PCONSOLE Console
,
958 IN OUT PVOID TitleBuffer
,
959 IN OUT PULONG BufLength
)
963 if (Console
== NULL
|| TitleBuffer
== NULL
|| BufLength
== NULL
)
964 return STATUS_INVALID_PARAMETER
;
966 /* Copy title of the console to the user title buffer */
969 if (*BufLength
>= sizeof(WCHAR
))
971 Length
= min(*BufLength
- sizeof(WCHAR
), Console
->Title
.Length
);
972 RtlCopyMemory(TitleBuffer
, Console
->Title
.Buffer
, Length
);
973 ((PWCHAR
)TitleBuffer
)[Length
/ sizeof(WCHAR
)] = L
'\0';
978 *BufLength
= Console
->Title
.Length
;
983 if (*BufLength
>= sizeof(CHAR
))
985 Length
= min(*BufLength
- sizeof(CHAR
), Console
->Title
.Length
/ sizeof(WCHAR
));
986 Length
= WideCharToMultiByte(Console
->CodePage
, 0,
987 Console
->Title
.Buffer
, Length
,
990 ((PCHAR
)TitleBuffer
)[Length
] = '\0';
995 *BufLength
= Console
->Title
.Length
/ sizeof(WCHAR
);
999 return STATUS_SUCCESS
;
1003 ConDrvSetConsoleTitle(IN PCONSOLE Console
,
1005 IN PVOID TitleBuffer
,
1011 if (Console
== NULL
|| TitleBuffer
== NULL
)
1012 return STATUS_INVALID_PARAMETER
;
1016 /* Length is in bytes */
1021 /* Use the console input CP for the conversion */
1022 Length
= MultiByteToWideChar(Console
->CodePage
, 0,
1023 TitleBuffer
, BufLength
,
1025 /* The returned Length was in number of wchars, convert it in bytes */
1026 Length
*= sizeof(WCHAR
);
1029 /* Allocate a new buffer to hold the new title (NULL-terminated) */
1030 Buffer
= ConsoleAllocHeap(HEAP_ZERO_MEMORY
, Length
+ sizeof(WCHAR
));
1031 if (!Buffer
) return STATUS_NO_MEMORY
;
1033 /* Free the old title */
1034 ConsoleFreeUnicodeString(&Console
->Title
);
1036 /* Copy title to console */
1037 Console
->Title
.Buffer
= Buffer
;
1038 Console
->Title
.Length
= Length
;
1039 Console
->Title
.MaximumLength
= Console
->Title
.Length
+ sizeof(WCHAR
);
1043 RtlCopyMemory(Console
->Title
.Buffer
, TitleBuffer
, Console
->Title
.Length
);
1047 MultiByteToWideChar(Console
->CodePage
, 0,
1048 TitleBuffer
, BufLength
,
1049 Console
->Title
.Buffer
,
1050 Console
->Title
.Length
/ sizeof(WCHAR
));
1053 /* NULL-terminate */
1054 Console
->Title
.Buffer
[Console
->Title
.Length
/ sizeof(WCHAR
)] = L
'\0';
1056 // TermChangeTitle(Console);
1057 return STATUS_SUCCESS
;
1061 ConDrvGetConsoleCP(IN PCONSOLE Console
,
1063 IN BOOLEAN OutputCP
)
1065 if (Console
== NULL
|| CodePage
== NULL
)
1066 return STATUS_INVALID_PARAMETER
;
1068 *CodePage
= (OutputCP
? Console
->OutputCodePage
: Console
->CodePage
);
1070 return STATUS_SUCCESS
;
1074 ConDrvSetConsoleCP(IN PCONSOLE Console
,
1076 IN BOOLEAN OutputCP
)
1078 if (Console
== NULL
|| !IsValidCodePage(CodePage
))
1079 return STATUS_INVALID_PARAMETER
;
1082 Console
->OutputCodePage
= CodePage
;
1084 Console
->CodePage
= CodePage
;
1086 return STATUS_SUCCESS
;
1089 PCONSOLE_PROCESS_DATA NTAPI
1090 ConDrvGetConsoleLeaderProcess(IN PCONSOLE Console
)
1092 if (Console
== NULL
) return NULL
;
1094 return CONTAINING_RECORD(Console
->ProcessList
.Blink
,
1095 CONSOLE_PROCESS_DATA
,
1100 ConDrvGetConsoleProcessList(IN PCONSOLE Console
,
1101 IN OUT PULONG ProcessIdsList
,
1102 IN ULONG MaxIdListItems
,
1103 OUT PULONG ProcessIdsTotal
)
1105 PCONSOLE_PROCESS_DATA current
;
1106 PLIST_ENTRY current_entry
;
1108 if (Console
== NULL
|| ProcessIdsList
== NULL
|| ProcessIdsTotal
== NULL
)
1109 return STATUS_INVALID_PARAMETER
;
1111 *ProcessIdsTotal
= 0;
1113 for (current_entry
= Console
->ProcessList
.Flink
;
1114 current_entry
!= &Console
->ProcessList
;
1115 current_entry
= current_entry
->Flink
)
1117 current
= CONTAINING_RECORD(current_entry
, CONSOLE_PROCESS_DATA
, ConsoleLink
);
1118 if (++(*ProcessIdsTotal
) <= MaxIdListItems
)
1120 *ProcessIdsList
++ = HandleToUlong(current
->Process
->ClientId
.UniqueProcess
);
1124 return STATUS_SUCCESS
;
1127 // ConDrvGenerateConsoleCtrlEvent
1129 ConDrvConsoleProcessCtrlEvent(IN PCONSOLE Console
,
1130 IN ULONG ProcessGroupId
,
1133 NTSTATUS Status
= STATUS_SUCCESS
;
1134 PLIST_ENTRY current_entry
;
1135 PCONSOLE_PROCESS_DATA current
;
1137 /* If the console is already being destroyed, just return */
1138 if (!ConDrvValidateConsoleState(Console
, CONSOLE_RUNNING
))
1139 return STATUS_UNSUCCESSFUL
;
1142 * Loop through the process list, from the most recent process
1143 * (the active one) to the oldest one (the first created, i.e.
1144 * the console leader process), and for each, send an event
1145 * (new processes are inserted at the head of the console process list).
1147 current_entry
= Console
->ProcessList
.Flink
;
1148 while (current_entry
!= &Console
->ProcessList
)
1150 current
= CONTAINING_RECORD(current_entry
, CONSOLE_PROCESS_DATA
, ConsoleLink
);
1151 current_entry
= current_entry
->Flink
;
1154 * Only processes belonging to the same process group are signaled.
1155 * If the process group ID is zero, then all the processes are signaled.
1157 if (ProcessGroupId
== 0 || current
->Process
->ProcessGroupId
== ProcessGroupId
)
1159 Status
= ConDrvConsoleCtrlEvent(CtrlEvent
, current
);