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 *******************************************************************/
14 #include "include/conio.h"
15 #include "include/conio2.h"
20 #include "conoutput.h"
21 #include "lineinput.h"
22 #include "include/settings.h"
24 #include "include/console.h"
31 // FIXME: Add this prototype to winternl.h / rtlfuncs.h / ...
32 NTSTATUS NTAPI
RtlGetLastNtStatus(VOID
);
35 /* GLOBALS ********************************************************************/
37 static ULONG ConsoleListSize
;
38 static PCONSOLE
* ConsoleList
; /* The list of all the allocated consoles */
39 static RTL_RESOURCE ListLock
;
41 #define ConDrvLockConsoleListExclusive() \
42 RtlAcquireResourceExclusive(&ListLock, TRUE)
44 #define ConDrvLockConsoleListShared() \
45 RtlAcquireResourceShared(&ListLock, TRUE)
47 #define ConDrvUnlockConsoleList() \
48 RtlReleaseResource(&ListLock)
50 // Adapted from reactos/lib/rtl/unicode.c, RtlCreateUnicodeString line 2180
52 ConsoleCreateUnicodeString(IN OUT PUNICODE_STRING UniDest
,
55 SIZE_T Size
= (wcslen(Source
) + 1) * sizeof(WCHAR
);
56 if (Size
> MAXUSHORT
) return FALSE
;
58 UniDest
->Buffer
= ConsoleAllocHeap(HEAP_ZERO_MEMORY
, Size
);
59 if (UniDest
->Buffer
== NULL
) return FALSE
;
61 RtlCopyMemory(UniDest
->Buffer
, Source
, Size
);
62 UniDest
->MaximumLength
= (USHORT
)Size
;
63 UniDest
->Length
= (USHORT
)Size
- sizeof(WCHAR
);
68 // Adapted from reactos/lib/rtl/unicode.c, RtlFreeUnicodeString line 431
70 ConsoleFreeUnicodeString(IN PUNICODE_STRING UnicodeString
)
72 if (UnicodeString
->Buffer
)
74 ConsoleFreeHeap(UnicodeString
->Buffer
);
75 RtlZeroMemory(UnicodeString
, sizeof(UNICODE_STRING
));
81 InsertConsole(OUT PHANDLE Handle
,
84 #define CONSOLE_HANDLES_INCREMENT 2 * 3
86 NTSTATUS Status
= STATUS_SUCCESS
;
90 ASSERT( (ConsoleList
== NULL
&& ConsoleListSize
== 0) ||
91 (ConsoleList
!= NULL
&& ConsoleListSize
!= 0) );
93 /* All went right, so add the console to the list */
94 ConDrvLockConsoleListExclusive();
95 DPRINT1("Insert in the list\n");
99 for (i
= 0; i
< ConsoleListSize
; i
++)
101 if (ConsoleList
[i
] == NULL
) break;
105 if (i
>= ConsoleListSize
)
107 DPRINT1("Creation of a new handles table\n");
108 /* Allocate a new handles table */
109 Block
= ConsoleAllocHeap(HEAP_ZERO_MEMORY
,
111 CONSOLE_HANDLES_INCREMENT
) * sizeof(PCONSOLE
));
114 Status
= STATUS_UNSUCCESSFUL
;
118 /* If we previously had a handles table, free it and use the new one */
121 /* Copy the handles from the old table to the new one */
124 ConsoleListSize
* sizeof(PCONSOLE
));
125 ConsoleFreeHeap(ConsoleList
);
128 ConsoleListSize
+= CONSOLE_HANDLES_INCREMENT
;
131 ConsoleList
[i
] = Console
;
132 *Handle
= ULongToHandle((i
<< 2) | 0x3);
135 /* Unlock the console list and return status */
136 ConDrvUnlockConsoleList();
143 RemoveConsoleByHandle(IN HANDLE Handle
)
145 NTSTATUS Status
= STATUS_SUCCESS
;
148 BOOLEAN ValidHandle
= ((HandleToULong(Handle
) & 0x3) == 0x3);
149 ULONG Index
= HandleToULong(Handle
) >> 2;
151 if (!ValidHandle
) return STATUS_INVALID_HANDLE
;
153 ASSERT( (ConsoleList
== NULL
&& ConsoleListSize
== 0) ||
154 (ConsoleList
!= NULL
&& ConsoleListSize
!= 0) );
156 /* Remove the console from the list */
157 ConDrvLockConsoleListExclusive();
159 if (Index
>= ConsoleListSize
||
160 (Console
= ConsoleList
[Index
]) == NULL
)
162 Status
= STATUS_INVALID_HANDLE
;
166 ConsoleList
[Index
] = NULL
;
169 /* Unlock the console list and return status */
170 ConDrvUnlockConsoleList();
176 RemoveConsoleByPointer(IN PCONSOLE Console
)
180 if (!Console
) return STATUS_INVALID_PARAMETER
;
182 ASSERT( (ConsoleList
== NULL
&& ConsoleListSize
== 0) ||
183 (ConsoleList
!= NULL
&& ConsoleListSize
!= 0) );
185 /* Remove the console from the list */
186 ConDrvLockConsoleListExclusive();
190 for (i
= 0; i
< ConsoleListSize
; i
++)
192 if (ConsoleList
[i
] == Console
) ConsoleList
[i
] = NULL
;
196 /* Unlock the console list */
197 ConDrvUnlockConsoleList();
199 return STATUS_SUCCESS
;
203 /* For resetting the frontend - defined in dummyfrontend.c */
204 VOID
ResetFrontEnd(IN PCONSOLE Console
);
207 /* PRIVATE FUNCTIONS **********************************************************/
210 ConDrvConsoleCtrlEventTimeout(IN ULONG Event
,
211 IN PCONSOLE_PROCESS_DATA ProcessData
,
214 NTSTATUS Status
= STATUS_SUCCESS
;
216 DPRINT("ConDrvConsoleCtrlEventTimeout Parent ProcessId = %x\n", ProcessData
->Process
->ClientId
.UniqueProcess
);
218 if (ProcessData
->CtrlDispatcher
)
222 HANDLE Thread
= NULL
;
226 Thread
= CreateRemoteThread(ProcessData
->Process
->ProcessHandle
, NULL
, 0,
227 ProcessData
->CtrlDispatcher
,
228 UlongToPtr(Event
), 0, NULL
);
231 Status
= RtlGetLastNtStatus();
232 DPRINT1("Failed thread creation, Status = 0x%08lx\n", Status
);
236 DPRINT("ProcessData->CtrlDispatcher remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n", ProcessData
->Process
->ClientId
.UniqueProcess
, ProcessData
->Process
);
237 WaitForSingleObject(Thread
, Timeout
);
246 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
248 Status
= _SEH2_GetExceptionCode();
249 DPRINT1("ConDrvConsoleCtrlEventTimeout - Caught an exception, Status = 0x%08lx\n", Status
);
258 ConDrvConsoleCtrlEvent(IN ULONG Event
,
259 IN PCONSOLE_PROCESS_DATA ProcessData
)
261 return ConDrvConsoleCtrlEventTimeout(Event
, ProcessData
, 0);
265 ConioPause(PCONSOLE Console
, UINT Flags
)
267 Console
->PauseFlags
|= Flags
;
268 if (!Console
->UnpauseEvent
)
269 Console
->UnpauseEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
273 ConioUnpause(PCONSOLE Console
, UINT Flags
)
275 Console
->PauseFlags
&= ~Flags
;
277 // if ((Console->PauseFlags & (PAUSED_FROM_KEYBOARD | PAUSED_FROM_SCROLLBAR | PAUSED_FROM_SELECTION)) == 0)
278 if (Console
->PauseFlags
== 0 && Console
->UnpauseEvent
)
280 SetEvent(Console
->UnpauseEvent
);
281 CloseHandle(Console
->UnpauseEvent
);
282 Console
->UnpauseEvent
= NULL
;
284 CsrNotifyWait(&Console
->WriteWaitQueue
,
288 if (!IsListEmpty(&Console
->WriteWaitQueue
))
290 CsrDereferenceWait(&Console
->WriteWaitQueue
);
297 * Console accessibility check helpers
301 ConDrvValidateConsoleState(IN PCONSOLE Console
,
302 IN CONSOLE_STATE ExpectedState
)
304 // if (!Console) return FALSE;
306 /* The console must be locked */
307 // ASSERT(Console_locked);
309 return (Console
->State
== ExpectedState
);
313 ConDrvValidateConsoleUnsafe(IN PCONSOLE Console
,
314 IN CONSOLE_STATE ExpectedState
,
315 IN BOOLEAN LockConsole
)
317 if (!Console
) return FALSE
;
320 * Lock the console to forbid possible console's state changes
321 * (which must be done when the console is already locked).
322 * If we don't want to lock it, it's because the lock is already
323 * held. So there must be no problems.
325 if (LockConsole
) EnterCriticalSection(&Console
->Lock
);
327 // ASSERT(Console_locked);
329 /* Check whether the console's state is what we expect */
330 if (!ConDrvValidateConsoleState(Console
, ExpectedState
))
332 if (LockConsole
) LeaveCriticalSection(&Console
->Lock
);
340 ConDrvValidateConsole(OUT PCONSOLE
* Console
,
341 IN HANDLE ConsoleHandle
,
342 IN CONSOLE_STATE ExpectedState
,
343 IN BOOLEAN LockConsole
)
345 BOOLEAN RetVal
= FALSE
;
346 PCONSOLE ValidatedConsole
;
348 BOOLEAN ValidHandle
= ((HandleToULong(ConsoleHandle
) & 0x3) == 0x3);
349 ULONG Index
= HandleToULong(ConsoleHandle
) >> 2;
351 if (!ValidHandle
) return FALSE
;
353 if (!Console
) return FALSE
;
357 * Forbid creation or deletion of consoles when
358 * checking for the existence of a console.
360 ConDrvLockConsoleListShared();
362 if (Index
>= ConsoleListSize
||
363 (ValidatedConsole
= ConsoleList
[Index
]) == NULL
)
365 /* Unlock the console list */
366 ConDrvUnlockConsoleList();
371 ValidatedConsole
= ConsoleList
[Index
];
373 /* Unlock the console list and return */
374 ConDrvUnlockConsoleList();
376 RetVal
= ConDrvValidateConsoleUnsafe(ValidatedConsole
,
379 if (RetVal
) *Console
= ValidatedConsole
;
385 ConDrvGetConsole(OUT PCONSOLE
* Console
,
386 IN HANDLE ConsoleHandle
,
387 IN BOOLEAN LockConsole
)
389 NTSTATUS Status
= STATUS_INVALID_HANDLE
;
390 PCONSOLE GrabConsole
;
392 if (Console
== NULL
) return STATUS_INVALID_PARAMETER
;
395 if (ConDrvValidateConsole(&GrabConsole
,
400 InterlockedIncrement(&GrabConsole
->ReferenceCount
);
401 *Console
= GrabConsole
;
402 Status
= STATUS_SUCCESS
;
409 ConDrvReleaseConsole(IN PCONSOLE Console
,
410 IN BOOLEAN WasConsoleLocked
)
414 if (!Console
) return;
415 // if (Console->ReferenceCount == 0) return; // This shouldn't happen
416 ASSERT(Console
->ReferenceCount
> 0);
418 /* The console must be locked */
419 // ASSERT(Console_locked);
422 * Decrement the reference count. Save the new value too,
423 * because Console->ReferenceCount might be modified after
424 * the console gets unlocked but before we check whether we
427 RefCount
= _InterlockedDecrement(&Console
->ReferenceCount
);
429 /* Unlock the console if needed */
430 if (WasConsoleLocked
) LeaveCriticalSection(&Console
->Lock
);
432 /* Delete the console if needed */
433 if (RefCount
<= 0) ConDrvDeleteConsole(Console
);
437 /* CONSOLE INITIALIZATION FUNCTIONS *******************************************/
440 ConDrvInitConsoleSupport(VOID
)
442 DPRINT("CONSRV: ConDrvInitConsoleSupport()\n");
444 /* Initialize the console list and its lock */
447 RtlInitializeResource(&ListLock
);
449 /* Should call LoadKeyboardLayout */
453 ConDrvInitConsole(OUT PHANDLE NewConsoleHandle
,
454 OUT PCONSOLE
* NewConsole
,
455 IN PCONSOLE_INFO ConsoleInfo
,
456 IN ULONG ConsoleLeaderProcessId
)
459 SECURITY_ATTRIBUTES SecurityAttributes
;
460 // CONSOLE_INFO CapturedConsoleInfo;
461 TEXTMODE_BUFFER_INFO ScreenBufferInfo
;
462 HANDLE ConsoleHandle
;
464 PCONSOLE_SCREEN_BUFFER NewBuffer
;
465 // WCHAR DefaultTitle[128];
467 if (NewConsoleHandle
== NULL
|| NewConsole
== NULL
|| ConsoleInfo
== NULL
)
468 return STATUS_INVALID_PARAMETER
;
470 *NewConsoleHandle
= NULL
;
474 * Allocate a console structure
476 Console
= ConsoleAllocHeap(HEAP_ZERO_MEMORY
, sizeof(CONSOLE
));
479 DPRINT1("Not enough memory for console creation.\n");
480 return STATUS_NO_MEMORY
;
484 * Load the console settings
487 /* 1. Load the default settings */
488 // ConSrvGetDefaultSettings(ConsoleInfo, ProcessId);
490 // /* 2. Get the title of the console (initialize ConsoleInfo.ConsoleTitle) */
491 // Length = min(wcslen(ConsoleStartInfo->ConsoleTitle),
492 // sizeof(ConsoleInfo.ConsoleTitle) / sizeof(ConsoleInfo.ConsoleTitle[0]) - 1);
493 // wcsncpy(ConsoleInfo.ConsoleTitle, ConsoleStartInfo->ConsoleTitle, Length);
494 // ConsoleInfo.ConsoleTitle[Length] = L'\0';
497 * 4. Load the remaining console settings via the registry.
500 if ((ConsoleStartInfo
->dwStartupFlags
& STARTF_TITLEISLINKNAME
) == 0)
503 * Either we weren't created by an app launched via a shell-link,
504 * or we failed to load shell-link console properties.
505 * Therefore, load the console infos for the application from the registry.
507 ConSrvReadUserSettings(ConsoleInfo
, ProcessId
);
510 * Now, update them with the properties the user might gave to us
511 * via the STARTUPINFO structure before calling CreateProcess
512 * (and which was transmitted via the ConsoleStartInfo structure).
513 * We therefore overwrite the values read in the registry.
515 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_USEFILLATTRIBUTE
)
517 ConsoleInfo
->ScreenAttrib
= (USHORT
)ConsoleStartInfo
->FillAttribute
;
519 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_USECOUNTCHARS
)
521 ConsoleInfo
->ScreenBufferSize
= ConsoleStartInfo
->ScreenBufferSize
;
523 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_USESIZE
)
525 // ConsoleInfo->ConsoleSize = ConsoleStartInfo->ConsoleWindowSize;
526 ConsoleInfo
->ConsoleSize
.X
= (SHORT
)ConsoleStartInfo
->ConsoleWindowSize
.cx
;
527 ConsoleInfo
->ConsoleSize
.Y
= (SHORT
)ConsoleStartInfo
->ConsoleWindowSize
.cy
;
533 * Fix the screen buffer size if needed. The rule is:
534 * ScreenBufferSize >= ConsoleSize
536 if (ConsoleInfo
->ScreenBufferSize
.X
< ConsoleInfo
->ConsoleSize
.X
)
537 ConsoleInfo
->ScreenBufferSize
.X
= ConsoleInfo
->ConsoleSize
.X
;
538 if (ConsoleInfo
->ScreenBufferSize
.Y
< ConsoleInfo
->ConsoleSize
.Y
)
539 ConsoleInfo
->ScreenBufferSize
.Y
= ConsoleInfo
->ConsoleSize
.Y
;
542 * Initialize the console
544 Console
->State
= CONSOLE_INITIALIZING
;
545 Console
->ReferenceCount
= 0;
546 InitializeCriticalSection(&Console
->Lock
);
547 InitializeListHead(&Console
->ProcessList
);
549 /* Initialize the frontend interface */
550 ResetFrontEnd(Console
);
552 memcpy(Console
->Colors
, ConsoleInfo
->Colors
, sizeof(ConsoleInfo
->Colors
));
553 Console
->ConsoleSize
= ConsoleInfo
->ConsoleSize
;
554 Console
->FixedSize
= FALSE
; // Value by default; is reseted by the front-ends if needed.
557 * Initialize the input buffer
559 ConSrvInitObject(&Console
->InputBuffer
.Header
, INPUT_BUFFER
, Console
);
561 SecurityAttributes
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
562 SecurityAttributes
.lpSecurityDescriptor
= NULL
;
563 SecurityAttributes
.bInheritHandle
= TRUE
;
564 Console
->InputBuffer
.ActiveEvent
= CreateEventW(&SecurityAttributes
, TRUE
, FALSE
, NULL
);
565 if (NULL
== Console
->InputBuffer
.ActiveEvent
)
567 DeleteCriticalSection(&Console
->Lock
);
568 ConsoleFreeHeap(Console
);
569 return STATUS_UNSUCCESSFUL
;
572 Console
->InputBuffer
.InputBufferSize
= 0; // FIXME!
573 InitializeListHead(&Console
->InputBuffer
.InputEvents
);
574 InitializeListHead(&Console
->InputBuffer
.ReadWaitQueue
);
575 Console
->InputBuffer
.Mode
= ENABLE_PROCESSED_INPUT
| ENABLE_LINE_INPUT
|
576 ENABLE_ECHO_INPUT
| ENABLE_MOUSE_INPUT
;
578 Console
->QuickEdit
= ConsoleInfo
->QuickEdit
;
579 Console
->InsertMode
= ConsoleInfo
->InsertMode
;
580 Console
->LineBuffer
= NULL
;
581 Console
->LineMaxSize
= Console
->LineSize
= Console
->LinePos
= 0;
582 Console
->LineComplete
= Console
->LineUpPressed
= Console
->LineInsertToggle
= FALSE
;
585 // FIXME: This is terminal-specific !! VV
586 RtlZeroMemory(&Console
->Selection
, sizeof(CONSOLE_SELECTION_INFO
));
587 Console
->Selection
.dwFlags
= CONSOLE_NO_SELECTION
;
590 /* Set-up the code page */
591 Console
->CodePage
= Console
->OutputCodePage
= ConsoleInfo
->CodePage
;
593 /* Initialize a new text-mode screen buffer with default settings */
594 ScreenBufferInfo
.ScreenBufferSize
= ConsoleInfo
->ScreenBufferSize
;
595 ScreenBufferInfo
.ScreenAttrib
= ConsoleInfo
->ScreenAttrib
;
596 ScreenBufferInfo
.PopupAttrib
= ConsoleInfo
->PopupAttrib
;
597 ScreenBufferInfo
.IsCursorVisible
= TRUE
;
598 ScreenBufferInfo
.CursorSize
= ConsoleInfo
->CursorSize
;
600 InitializeListHead(&Console
->BufferList
);
601 Status
= ConDrvCreateScreenBuffer(&NewBuffer
,
603 CONSOLE_TEXTMODE_BUFFER
,
605 if (!NT_SUCCESS(Status
))
607 DPRINT1("ConDrvCreateScreenBuffer: failed, Status = 0x%08lx\n", Status
);
608 CloseHandle(Console
->InputBuffer
.ActiveEvent
);
609 DeleteCriticalSection(&Console
->Lock
);
610 ConsoleFreeHeap(Console
);
613 /* Make the new screen buffer active */
614 Console
->ActiveBuffer
= NewBuffer
;
615 InitializeListHead(&Console
->WriteWaitQueue
);
616 Console
->PauseFlags
= 0;
617 Console
->UnpauseEvent
= NULL
;
620 * Initialize the alias and history buffers
622 Console
->Aliases
= NULL
;
623 InitializeListHead(&Console
->HistoryBuffers
);
624 Console
->HistoryBufferSize
= ConsoleInfo
->HistoryBufferSize
;
625 Console
->NumberOfHistoryBuffers
= ConsoleInfo
->NumberOfHistoryBuffers
;
626 Console
->HistoryNoDup
= ConsoleInfo
->HistoryNoDup
;
628 /* Initialize the console title */
629 ConsoleCreateUnicodeString(&Console
->OriginalTitle
, ConsoleInfo
->ConsoleTitle
);
630 // if (ConsoleInfo.ConsoleTitle[0] == L'\0')
632 // if (LoadStringW(ConSrvDllInstance, IDS_CONSOLE_TITLE, DefaultTitle, sizeof(DefaultTitle) / sizeof(DefaultTitle[0])))
634 // ConsoleCreateUnicodeString(&Console->Title, DefaultTitle);
638 // ConsoleCreateUnicodeString(&Console->Title, L"ReactOS Console");
643 ConsoleCreateUnicodeString(&Console
->Title
, ConsoleInfo
->ConsoleTitle
);
646 /* Lock the console until its initialization is finished */
647 // EnterCriticalSection(&Console->Lock);
649 DPRINT("Console initialized\n");
651 /* All went right, so add the console to the list */
652 Status
= InsertConsole(&ConsoleHandle
, Console
);
653 if (!NT_SUCCESS(Status
))
656 ConDrvDeleteConsole(Console
);
660 /* The initialization is finished */
661 DPRINT("Change state\n");
662 Console
->State
= CONSOLE_RUNNING
;
664 /* Unlock the console */
665 // LeaveCriticalSection(&Console->Lock);
667 /* Return the newly created console to the caller and a success code too */
668 *NewConsoleHandle
= ConsoleHandle
;
669 *NewConsole
= Console
;
670 return STATUS_SUCCESS
;
674 ConDrvRegisterFrontEnd(IN PCONSOLE Console
,
675 IN PFRONTEND FrontEnd
)
679 if (Console
== NULL
|| FrontEnd
== NULL
)
680 return STATUS_INVALID_PARAMETER
;
682 /* FIXME: Lock the console before ?? */
685 * Attach the frontend to the console. Use now the TermIFace of the console,
686 * and not the user-defined temporary FrontEnd pointer.
688 Console
->TermIFace
= *FrontEnd
;
689 Console
->TermIFace
.Console
= Console
;
691 /* Initialize the frontend AFTER having attached it to the console */
692 DPRINT("Finish initialization of frontend\n");
693 Status
= Console
->TermIFace
.Vtbl
->InitFrontEnd(&Console
->TermIFace
, Console
);
694 if (!NT_SUCCESS(Status
))
696 DPRINT1("FrontEnd initialization failed, Status = 0x%08lx\n", Status
);
698 /* We failed, detach the frontend from the console */
699 FrontEnd
->Console
= NULL
; // For the caller
700 ResetFrontEnd(Console
);
705 /* Copy buffer contents to screen */
707 // ConioDrawConsole(Console);
708 DPRINT("Console drawn\n");
710 DPRINT("Terminal FrontEnd initialization done\n");
711 return STATUS_SUCCESS
;
715 ConDrvDeregisterFrontEnd(IN PCONSOLE Console
)
717 if (Console
== NULL
) return STATUS_INVALID_PARAMETER
;
719 /* FIXME: Lock the console before ?? */
721 /* Deinitialize the frontend BEFORE detaching it from the console */
722 Console
->TermIFace
.Vtbl
->DeinitFrontEnd(&Console
->TermIFace
/*, Console*/);
725 * Detach the frontend from the console:
726 * reinitialize the frontend interface.
728 ResetFrontEnd(Console
);
730 DPRINT("Terminal FrontEnd unregistered\n");
731 return STATUS_SUCCESS
;
735 ConDrvDeleteConsole(IN PCONSOLE Console
)
737 DPRINT("ConDrvDeleteConsole(0x%p)\n", Console
);
740 * Forbid validation of any console by other threads
741 * during the deletion of this console.
743 ConDrvLockConsoleListExclusive();
746 * If the console is already being destroyed, i.e. not running
747 * or finishing to be initialized, just return.
749 if (!ConDrvValidateConsoleUnsafe(Console
, CONSOLE_RUNNING
, TRUE
) &&
750 !ConDrvValidateConsoleUnsafe(Console
, CONSOLE_INITIALIZING
, TRUE
))
752 /* Unlock the console list and return */
753 ConDrvUnlockConsoleList();
758 * We are about to be destroyed. Signal it to other people
759 * so that they can terminate what they are doing, and that
760 * they cannot longer validate the console.
762 Console
->State
= CONSOLE_TERMINATING
;
765 * Allow other threads to finish their job: basically, unlock
766 * all other calls to EnterCriticalSection(&Console->Lock); by
767 * ConDrvValidateConsole(Unsafe) functions so that they just see
768 * that we are not in CONSOLE_RUNNING state anymore, or unlock
769 * other concurrent calls to ConDrvDeleteConsole so that they
770 * can see that we are in fact already deleting the console.
772 LeaveCriticalSection(&Console
->Lock
);
773 ConDrvUnlockConsoleList();
775 /* FIXME: Send a terminate message to all the processes owning this console */
777 /* Cleanup the UI-oriented part */
778 DPRINT("Deregister console\n");
779 ConDrvDeregisterFrontEnd(Console
);
780 DPRINT("Console deregistered\n");
783 * Check that the console is in terminating state before continuing
784 * (the cleanup code must not change the state of the console...
785 * ...unless to cancel console deletion ?).
788 ConDrvLockConsoleListExclusive();
790 if (!ConDrvValidateConsoleUnsafe(Console
, CONSOLE_TERMINATING
, TRUE
))
792 ConDrvUnlockConsoleList();
796 /* We are now in destruction */
797 Console
->State
= CONSOLE_IN_DESTRUCTION
;
799 /* We really delete the console. Reset the count to be sure. */
800 Console
->ReferenceCount
= 0;
802 /* Remove the console from the list */
803 RemoveConsoleByPointer(Console
);
805 /* Discard all entries in the input event queue */
806 PurgeInputBuffer(Console
);
808 if (Console
->LineBuffer
) ConsoleFreeHeap(Console
->LineBuffer
);
810 IntDeleteAllAliases(Console
);
811 HistoryDeleteBuffers(Console
);
813 ConioDeleteScreenBuffer(Console
->ActiveBuffer
);
814 Console
->ActiveBuffer
= NULL
;
815 if (!IsListEmpty(&Console
->BufferList
))
817 DPRINT1("BUG: screen buffer list not empty\n");
821 /**/ CloseHandle(Console
->InputBuffer
.ActiveEvent
); /**/
822 if (Console
->UnpauseEvent
) CloseHandle(Console
->UnpauseEvent
);
824 ConsoleFreeUnicodeString(&Console
->OriginalTitle
);
825 ConsoleFreeUnicodeString(&Console
->Title
);
827 DPRINT("ConDrvDeleteConsole - Unlocking\n");
828 LeaveCriticalSection(&Console
->Lock
);
829 DPRINT("ConDrvDeleteConsole - Destroying lock\n");
830 DeleteCriticalSection(&Console
->Lock
);
831 DPRINT("ConDrvDeleteConsole - Lock destroyed ; freeing console\n");
833 ConsoleFreeHeap(Console
);
834 DPRINT("ConDrvDeleteConsole - Console destroyed\n");
836 /* Unlock the console list and return */
837 ConDrvUnlockConsoleList();
841 /* PUBLIC DRIVER APIS *********************************************************/
844 ConDrvGetConsoleMode(IN PCONSOLE Console
,
845 IN PCONSOLE_IO_OBJECT Object
,
846 OUT PULONG ConsoleMode
)
848 NTSTATUS Status
= STATUS_SUCCESS
;
850 if (Console
== NULL
|| Object
== NULL
|| ConsoleMode
== NULL
)
851 return STATUS_INVALID_PARAMETER
;
854 ASSERT(Console
== Object
->Console
);
856 /*** FIXME: */ *ConsoleMode
= 0; /***/
858 if (INPUT_BUFFER
== Object
->Type
)
860 PCONSOLE_INPUT_BUFFER InputBuffer
= (PCONSOLE_INPUT_BUFFER
)Object
;
862 *ConsoleMode
= InputBuffer
->Mode
;
864 if (Console
->QuickEdit
|| Console
->InsertMode
)
866 // Windows does this, even if it's not documented on MSDN
867 *ConsoleMode
|= ENABLE_EXTENDED_FLAGS
;
869 if (Console
->QuickEdit
) *ConsoleMode
|= ENABLE_QUICK_EDIT_MODE
;
870 if (Console
->InsertMode
) *ConsoleMode
|= ENABLE_INSERT_MODE
;
873 else if (TEXTMODE_BUFFER
== Object
->Type
|| GRAPHICS_BUFFER
== Object
->Type
)
875 PCONSOLE_SCREEN_BUFFER Buffer
= (PCONSOLE_SCREEN_BUFFER
)Object
;
876 *ConsoleMode
= Buffer
->Mode
;
880 Status
= STATUS_INVALID_HANDLE
;
887 ConDrvSetConsoleMode(IN PCONSOLE Console
,
888 IN PCONSOLE_IO_OBJECT Object
,
889 IN ULONG ConsoleMode
)
891 #define CONSOLE_VALID_CONTROL_MODES ( ENABLE_EXTENDED_FLAGS | ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE )
892 #define CONSOLE_VALID_INPUT_MODES ( ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | \
893 ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT | \
895 #define CONSOLE_VALID_OUTPUT_MODES ( ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT )
897 NTSTATUS Status
= STATUS_SUCCESS
;
899 if (Console
== NULL
|| Object
== NULL
)
900 return STATUS_INVALID_PARAMETER
;
903 ASSERT(Console
== Object
->Console
);
905 if (INPUT_BUFFER
== Object
->Type
)
907 PCONSOLE_INPUT_BUFFER InputBuffer
= (PCONSOLE_INPUT_BUFFER
)Object
;
909 DPRINT("SetConsoleMode(Input, %d)\n", ConsoleMode
);
912 * 1. Only the presence of valid mode flags is allowed.
914 if (ConsoleMode
& ~(CONSOLE_VALID_INPUT_MODES
| CONSOLE_VALID_CONTROL_MODES
))
916 Status
= STATUS_INVALID_PARAMETER
;
921 * 2. If we use control mode flags without ENABLE_EXTENDED_FLAGS,
922 * then consider the flags invalid.
924 if ( (ConsoleMode & CONSOLE_VALID_CONTROL_MODES) &&
925 (ConsoleMode & ENABLE_EXTENDED_FLAGS) == 0 )
927 Status = STATUS_INVALID_PARAMETER;
933 * 3. Now we can continue.
935 if (ConsoleMode
& CONSOLE_VALID_CONTROL_MODES
)
937 Console
->QuickEdit
= !!(ConsoleMode
& ENABLE_QUICK_EDIT_MODE
);
938 Console
->InsertMode
= !!(ConsoleMode
& ENABLE_INSERT_MODE
);
940 InputBuffer
->Mode
= (ConsoleMode
& CONSOLE_VALID_INPUT_MODES
);
942 else if (TEXTMODE_BUFFER
== Object
->Type
|| GRAPHICS_BUFFER
== Object
->Type
)
944 PCONSOLE_SCREEN_BUFFER Buffer
= (PCONSOLE_SCREEN_BUFFER
)Object
;
946 DPRINT("SetConsoleMode(Output, %d)\n", ConsoleMode
);
948 if (ConsoleMode
& ~CONSOLE_VALID_OUTPUT_MODES
)
950 Status
= STATUS_INVALID_PARAMETER
;
954 Buffer
->Mode
= (ConsoleMode
& CONSOLE_VALID_OUTPUT_MODES
);
959 Status
= STATUS_INVALID_HANDLE
;
967 ConDrvGetConsoleTitle(IN PCONSOLE Console
,
969 IN OUT PULONG BufLength
)
973 if (Console
== NULL
|| Title
== NULL
|| BufLength
== NULL
)
974 return STATUS_INVALID_PARAMETER
;
976 /* Copy title of the console to the user title buffer */
977 if (*BufLength
>= sizeof(WCHAR
))
979 Length
= min(*BufLength
- sizeof(WCHAR
), Console
->Title
.Length
);
980 RtlCopyMemory(Title
, Console
->Title
.Buffer
, Length
);
981 Title
[Length
/ sizeof(WCHAR
)] = L
'\0';
984 *BufLength
= Console
->Title
.Length
;
986 return STATUS_SUCCESS
;
990 ConDrvSetConsoleTitle(IN PCONSOLE Console
,
996 if (Console
== NULL
|| Title
== NULL
)
997 return STATUS_INVALID_PARAMETER
;
999 /* Allocate a new buffer to hold the new title (NULL-terminated) */
1000 Buffer
= ConsoleAllocHeap(0, BufLength
+ sizeof(WCHAR
));
1001 if (!Buffer
) return STATUS_NO_MEMORY
;
1003 /* Free the old title */
1004 ConsoleFreeUnicodeString(&Console
->Title
);
1006 /* Copy title to console */
1007 Console
->Title
.Buffer
= Buffer
;
1008 Console
->Title
.Length
= BufLength
;
1009 Console
->Title
.MaximumLength
= Console
->Title
.Length
+ sizeof(WCHAR
);
1010 RtlCopyMemory(Console
->Title
.Buffer
, Title
, Console
->Title
.Length
);
1011 Console
->Title
.Buffer
[Console
->Title
.Length
/ sizeof(WCHAR
)] = L
'\0';
1013 // ConioChangeTitle(Console);
1014 return STATUS_SUCCESS
;
1018 ConDrvGetConsoleCP(IN PCONSOLE Console
,
1022 if (Console
== NULL
|| CodePage
== NULL
)
1023 return STATUS_INVALID_PARAMETER
;
1025 *CodePage
= (InputCP
? Console
->CodePage
: Console
->OutputCodePage
);
1027 return STATUS_SUCCESS
;
1031 ConDrvSetConsoleCP(IN PCONSOLE Console
,
1035 if (Console
== NULL
|| !IsValidCodePage(CodePage
))
1036 return STATUS_INVALID_PARAMETER
;
1039 Console
->CodePage
= CodePage
;
1041 Console
->OutputCodePage
= CodePage
;
1043 return STATUS_SUCCESS
;
1047 ConDrvGetConsoleProcessList(IN PCONSOLE Console
,
1048 IN OUT PULONG ProcessIdsList
,
1049 IN ULONG MaxIdListItems
,
1050 OUT PULONG ProcessIdsTotal
)
1052 PCONSOLE_PROCESS_DATA current
;
1053 PLIST_ENTRY current_entry
;
1055 if (Console
== NULL
|| ProcessIdsList
== NULL
|| ProcessIdsTotal
== NULL
)
1056 return STATUS_INVALID_PARAMETER
;
1058 *ProcessIdsTotal
= 0;
1060 for (current_entry
= Console
->ProcessList
.Flink
;
1061 current_entry
!= &Console
->ProcessList
;
1062 current_entry
= current_entry
->Flink
)
1064 current
= CONTAINING_RECORD(current_entry
, CONSOLE_PROCESS_DATA
, ConsoleLink
);
1065 if (++(*ProcessIdsTotal
) <= MaxIdListItems
)
1067 *ProcessIdsList
++ = HandleToUlong(current
->Process
->ClientId
.UniqueProcess
);
1071 return STATUS_SUCCESS
;
1074 // ConDrvGenerateConsoleCtrlEvent
1076 ConDrvConsoleProcessCtrlEvent(IN PCONSOLE Console
,
1077 IN ULONG ProcessGroupId
,
1080 NTSTATUS Status
= STATUS_SUCCESS
;
1081 PLIST_ENTRY current_entry
;
1082 PCONSOLE_PROCESS_DATA current
;
1084 /* If the console is already being destroyed, just return */
1085 if (!ConDrvValidateConsoleState(Console
, CONSOLE_RUNNING
))
1086 return STATUS_UNSUCCESSFUL
;
1089 * Loop through the process list, from the most recent process
1090 * (the active one) to the oldest one (the first created, i.e.
1091 * the console leader process), and for each, send an event
1092 * (new processes are inserted at the head of the console process list).
1094 current_entry
= Console
->ProcessList
.Flink
;
1095 while (current_entry
!= &Console
->ProcessList
)
1097 current
= CONTAINING_RECORD(current_entry
, CONSOLE_PROCESS_DATA
, ConsoleLink
);
1098 current_entry
= current_entry
->Flink
;
1101 * Only processes belonging to the same process group are signaled.
1102 * If the process group ID is zero, then all the processes are signaled.
1104 if (ProcessGroupId
== 0 || current
->Process
->ProcessGroupId
== ProcessGroupId
)
1106 Status
= ConDrvConsoleCtrlEvent(Event
, current
);