fa4562f2ef582c17ce32348ca07db1ce9c399a3e
[reactos.git] / reactos / 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: win32ss/user/winsrv/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 #include "include/conio.h"
15 #include "include/conio2.h"
16 #include "handle.h"
17 #include "procinit.h"
18 #include "alias.h"
19 #include "coninput.h"
20 #include "conoutput.h"
21 #include "lineinput.h"
22 #include "include/settings.h"
23
24 #include "include/console.h"
25 #include "console.h"
26 #include "resource.h"
27
28 #define NDEBUG
29 #include <debug.h>
30
31 // FIXME: Add this prototype to winternl.h / rtlfuncs.h / ...
32 NTSTATUS NTAPI RtlGetLastNtStatus(VOID);
33
34
35 /* GLOBALS ********************************************************************/
36
37 static ULONG ConsoleListSize;
38 static PCONSOLE* ConsoleList; /* The list of all the allocated consoles */
39 static RTL_RESOURCE ListLock;
40
41 #define ConDrvLockConsoleListExclusive() \
42 RtlAcquireResourceExclusive(&ListLock, TRUE)
43
44 #define ConDrvLockConsoleListShared() \
45 RtlAcquireResourceShared(&ListLock, TRUE)
46
47 #define ConDrvUnlockConsoleList() \
48 RtlReleaseResource(&ListLock)
49
50 // Adapted from reactos/lib/rtl/unicode.c, RtlCreateUnicodeString line 2180
51 static BOOLEAN
52 ConsoleCreateUnicodeString(IN OUT PUNICODE_STRING UniDest,
53 IN PCWSTR Source)
54 {
55 SIZE_T Size = (wcslen(Source) + 1) * sizeof(WCHAR);
56 if (Size > MAXUSHORT) return FALSE;
57
58 UniDest->Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Size);
59 if (UniDest->Buffer == NULL) return FALSE;
60
61 RtlCopyMemory(UniDest->Buffer, Source, Size);
62 UniDest->MaximumLength = (USHORT)Size;
63 UniDest->Length = (USHORT)Size - sizeof(WCHAR);
64
65 return TRUE;
66 }
67
68 // Adapted from reactos/lib/rtl/unicode.c, RtlFreeUnicodeString line 431
69 static VOID
70 ConsoleFreeUnicodeString(IN PUNICODE_STRING UnicodeString)
71 {
72 if (UnicodeString->Buffer)
73 {
74 ConsoleFreeHeap(UnicodeString->Buffer);
75 RtlZeroMemory(UnicodeString, sizeof(UNICODE_STRING));
76 }
77 }
78
79
80 static NTSTATUS
81 InsertConsole(OUT PHANDLE Handle,
82 IN PCONSOLE Console)
83 {
84 #define CONSOLE_HANDLES_INCREMENT 2 * 3
85
86 NTSTATUS Status = STATUS_SUCCESS;
87 ULONG i = 0;
88 PCONSOLE* Block;
89
90 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
91 (ConsoleList != NULL && ConsoleListSize != 0) );
92
93 /* All went right, so add the console to the list */
94 ConDrvLockConsoleListExclusive();
95 DPRINT1("Insert in the list\n");
96
97 if (ConsoleList)
98 {
99 for (i = 0; i < ConsoleListSize; i++)
100 {
101 if (ConsoleList[i] == NULL) break;
102 }
103 }
104
105 if (i >= ConsoleListSize)
106 {
107 DPRINT1("Creation of a new handles table\n");
108 /* Allocate a new handles table */
109 Block = ConsoleAllocHeap(HEAP_ZERO_MEMORY,
110 (ConsoleListSize +
111 CONSOLE_HANDLES_INCREMENT) * sizeof(PCONSOLE));
112 if (Block == NULL)
113 {
114 Status = STATUS_UNSUCCESSFUL;
115 goto Quit;
116 }
117
118 /* If we previously had a handles table, free it and use the new one */
119 if (ConsoleList)
120 {
121 /* Copy the handles from the old table to the new one */
122 RtlCopyMemory(Block,
123 ConsoleList,
124 ConsoleListSize * sizeof(PCONSOLE));
125 ConsoleFreeHeap(ConsoleList);
126 }
127 ConsoleList = Block;
128 ConsoleListSize += CONSOLE_HANDLES_INCREMENT;
129 }
130
131 ConsoleList[i] = Console;
132 *Handle = ULongToHandle((i << 2) | 0x3);
133
134 Quit:
135 /* Unlock the console list and return status */
136 ConDrvUnlockConsoleList();
137 return Status;
138 }
139
140 /* Unused */
141 #if 0
142 static NTSTATUS
143 RemoveConsoleByHandle(IN HANDLE Handle)
144 {
145 NTSTATUS Status = STATUS_SUCCESS;
146 PCONSOLE Console;
147
148 BOOLEAN ValidHandle = ((HandleToULong(Handle) & 0x3) == 0x3);
149 ULONG Index = HandleToULong(Handle) >> 2;
150
151 if (!ValidHandle) return STATUS_INVALID_HANDLE;
152
153 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
154 (ConsoleList != NULL && ConsoleListSize != 0) );
155
156 /* Remove the console from the list */
157 ConDrvLockConsoleListExclusive();
158
159 if (Index >= ConsoleListSize ||
160 (Console = ConsoleList[Index]) == NULL)
161 {
162 Status = STATUS_INVALID_HANDLE;
163 goto Quit;
164 }
165
166 ConsoleList[Index] = NULL;
167
168 Quit:
169 /* Unlock the console list and return status */
170 ConDrvUnlockConsoleList();
171 return Status;
172 }
173 #endif
174
175 static NTSTATUS
176 RemoveConsoleByPointer(IN PCONSOLE Console)
177 {
178 ULONG i = 0;
179
180 if (!Console) return STATUS_INVALID_PARAMETER;
181
182 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
183 (ConsoleList != NULL && ConsoleListSize != 0) );
184
185 /* Remove the console from the list */
186 ConDrvLockConsoleListExclusive();
187
188 if (ConsoleList)
189 {
190 for (i = 0; i < ConsoleListSize; i++)
191 {
192 if (ConsoleList[i] == Console) ConsoleList[i] = NULL;
193 }
194 }
195
196 /* Unlock the console list */
197 ConDrvUnlockConsoleList();
198
199 return STATUS_SUCCESS;
200 }
201
202
203 /* For resetting the frontend - defined in dummyfrontend.c */
204 VOID ResetFrontEnd(IN PCONSOLE Console);
205
206
207 /* PRIVATE FUNCTIONS **********************************************************/
208
209 static NTSTATUS
210 ConDrvConsoleCtrlEventTimeout(IN ULONG Event,
211 IN PCONSOLE_PROCESS_DATA ProcessData,
212 IN ULONG Timeout)
213 {
214 NTSTATUS Status = STATUS_SUCCESS;
215
216 DPRINT("ConDrvConsoleCtrlEventTimeout Parent ProcessId = %x\n", ProcessData->Process->ClientId.UniqueProcess);
217
218 if (ProcessData->CtrlDispatcher)
219 {
220 _SEH2_TRY
221 {
222 HANDLE Thread = NULL;
223
224 _SEH2_TRY
225 {
226 Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0,
227 ProcessData->CtrlDispatcher,
228 UlongToPtr(Event), 0, NULL);
229 if (NULL == Thread)
230 {
231 Status = RtlGetLastNtStatus();
232 DPRINT1("Failed thread creation, Status = 0x%08lx\n", Status);
233 }
234 else
235 {
236 DPRINT("ProcessData->CtrlDispatcher remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n", ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process);
237 WaitForSingleObject(Thread, Timeout);
238 }
239 }
240 _SEH2_FINALLY
241 {
242 CloseHandle(Thread);
243 }
244 _SEH2_END;
245 }
246 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
247 {
248 Status = _SEH2_GetExceptionCode();
249 DPRINT1("ConDrvConsoleCtrlEventTimeout - Caught an exception, Status = 0x%08lx\n", Status);
250 }
251 _SEH2_END;
252 }
253
254 return Status;
255 }
256
257 static NTSTATUS
258 ConDrvConsoleCtrlEvent(IN ULONG Event,
259 IN PCONSOLE_PROCESS_DATA ProcessData)
260 {
261 return ConDrvConsoleCtrlEventTimeout(Event, ProcessData, 0);
262 }
263
264 VOID FASTCALL
265 ConioPause(PCONSOLE Console, UINT Flags)
266 {
267 Console->PauseFlags |= Flags;
268 if (!Console->UnpauseEvent)
269 Console->UnpauseEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
270 }
271
272 VOID FASTCALL
273 ConioUnpause(PCONSOLE Console, UINT Flags)
274 {
275 Console->PauseFlags &= ~Flags;
276
277 // if ((Console->PauseFlags & (PAUSED_FROM_KEYBOARD | PAUSED_FROM_SCROLLBAR | PAUSED_FROM_SELECTION)) == 0)
278 if (Console->PauseFlags == 0 && Console->UnpauseEvent)
279 {
280 SetEvent(Console->UnpauseEvent);
281 CloseHandle(Console->UnpauseEvent);
282 Console->UnpauseEvent = NULL;
283
284 CsrNotifyWait(&Console->WriteWaitQueue,
285 TRUE,
286 NULL,
287 NULL);
288 if (!IsListEmpty(&Console->WriteWaitQueue))
289 {
290 CsrDereferenceWait(&Console->WriteWaitQueue);
291 }
292 }
293 }
294
295
296 /*
297 * Console accessibility check helpers
298 */
299
300 BOOLEAN NTAPI
301 ConDrvValidateConsoleState(IN PCONSOLE Console,
302 IN CONSOLE_STATE ExpectedState)
303 {
304 // if (!Console) return FALSE;
305
306 /* The console must be locked */
307 // ASSERT(Console_locked);
308
309 return (Console->State == ExpectedState);
310 }
311
312 BOOLEAN NTAPI
313 ConDrvValidateConsoleUnsafe(IN PCONSOLE Console,
314 IN CONSOLE_STATE ExpectedState,
315 IN BOOLEAN LockConsole)
316 {
317 if (!Console) return FALSE;
318
319 /*
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.
324 */
325 if (LockConsole) EnterCriticalSection(&Console->Lock);
326
327 // ASSERT(Console_locked);
328
329 /* Check whether the console's state is what we expect */
330 if (!ConDrvValidateConsoleState(Console, ExpectedState))
331 {
332 if (LockConsole) LeaveCriticalSection(&Console->Lock);
333 return FALSE;
334 }
335
336 return TRUE;
337 }
338
339 BOOLEAN NTAPI
340 ConDrvValidateConsole(OUT PCONSOLE* Console,
341 IN HANDLE ConsoleHandle,
342 IN CONSOLE_STATE ExpectedState,
343 IN BOOLEAN LockConsole)
344 {
345 BOOLEAN RetVal = FALSE;
346 PCONSOLE ValidatedConsole;
347
348 BOOLEAN ValidHandle = ((HandleToULong(ConsoleHandle) & 0x3) == 0x3);
349 ULONG Index = HandleToULong(ConsoleHandle) >> 2;
350
351 if (!ValidHandle) return FALSE;
352
353 if (!Console) return FALSE;
354 *Console = NULL;
355
356 /*
357 * Forbid creation or deletion of consoles when
358 * checking for the existence of a console.
359 */
360 ConDrvLockConsoleListShared();
361
362 if (Index >= ConsoleListSize ||
363 (ValidatedConsole = ConsoleList[Index]) == NULL)
364 {
365 /* Unlock the console list */
366 ConDrvUnlockConsoleList();
367
368 return FALSE;
369 }
370
371 ValidatedConsole = ConsoleList[Index];
372
373 /* Unlock the console list and return */
374 ConDrvUnlockConsoleList();
375
376 RetVal = ConDrvValidateConsoleUnsafe(ValidatedConsole,
377 ExpectedState,
378 LockConsole);
379 if (RetVal) *Console = ValidatedConsole;
380
381 return RetVal;
382 }
383
384 NTSTATUS NTAPI
385 ConDrvGetConsole(OUT PCONSOLE* Console,
386 IN HANDLE ConsoleHandle,
387 IN BOOLEAN LockConsole)
388 {
389 NTSTATUS Status = STATUS_INVALID_HANDLE;
390 PCONSOLE GrabConsole;
391
392 if (Console == NULL) return STATUS_INVALID_PARAMETER;
393 *Console = NULL;
394
395 if (ConDrvValidateConsole(&GrabConsole,
396 ConsoleHandle,
397 CONSOLE_RUNNING,
398 LockConsole))
399 {
400 InterlockedIncrement(&GrabConsole->ReferenceCount);
401 *Console = GrabConsole;
402 Status = STATUS_SUCCESS;
403 }
404
405 return Status;
406 }
407
408 VOID NTAPI
409 ConDrvReleaseConsole(IN PCONSOLE Console,
410 IN BOOLEAN WasConsoleLocked)
411 {
412 LONG RefCount = 0;
413
414 if (!Console) return;
415 // if (Console->ReferenceCount == 0) return; // This shouldn't happen
416 ASSERT(Console->ReferenceCount > 0);
417
418 /* The console must be locked */
419 // ASSERT(Console_locked);
420
421 /*
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
425 * can destroy it.
426 */
427 RefCount = _InterlockedDecrement(&Console->ReferenceCount);
428
429 /* Unlock the console if needed */
430 if (WasConsoleLocked) LeaveCriticalSection(&Console->Lock);
431
432 /* Delete the console if needed */
433 if (RefCount <= 0) ConDrvDeleteConsole(Console);
434 }
435
436
437 /* CONSOLE INITIALIZATION FUNCTIONS *******************************************/
438
439 VOID NTAPI
440 ConDrvInitConsoleSupport(VOID)
441 {
442 DPRINT("CONSRV: ConDrvInitConsoleSupport()\n");
443
444 /* Initialize the console list and its lock */
445 ConsoleListSize = 0;
446 ConsoleList = NULL;
447 RtlInitializeResource(&ListLock);
448
449 /* Should call LoadKeyboardLayout */
450 }
451
452 NTSTATUS NTAPI
453 ConDrvInitConsole(OUT PHANDLE NewConsoleHandle,
454 OUT PCONSOLE* NewConsole,
455 IN PCONSOLE_INFO ConsoleInfo,
456 IN ULONG ConsoleLeaderProcessId)
457 {
458 NTSTATUS Status;
459 SECURITY_ATTRIBUTES SecurityAttributes;
460 // CONSOLE_INFO CapturedConsoleInfo;
461 TEXTMODE_BUFFER_INFO ScreenBufferInfo;
462 HANDLE ConsoleHandle;
463 PCONSOLE Console;
464 PCONSOLE_SCREEN_BUFFER NewBuffer;
465 // WCHAR DefaultTitle[128];
466
467 if (NewConsoleHandle == NULL || NewConsole == NULL || ConsoleInfo == NULL)
468 return STATUS_INVALID_PARAMETER;
469
470 *NewConsoleHandle = NULL;
471 *NewConsole = NULL;
472
473 /*
474 * Allocate a console structure
475 */
476 Console = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(CONSOLE));
477 if (NULL == Console)
478 {
479 DPRINT1("Not enough memory for console creation.\n");
480 return STATUS_NO_MEMORY;
481 }
482
483 /*
484 * Load the console settings
485 */
486
487 /* 1. Load the default settings */
488 // ConSrvGetDefaultSettings(ConsoleInfo, ProcessId);
489
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';
495
496 /*
497 * 4. Load the remaining console settings via the registry.
498 */
499 #if 0
500 if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
501 {
502 /*
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.
506 */
507 ConSrvReadUserSettings(ConsoleInfo, ProcessId);
508
509 /*
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.
514 */
515 if (ConsoleStartInfo->dwStartupFlags & STARTF_USEFILLATTRIBUTE)
516 {
517 ConsoleInfo->ScreenAttrib = (USHORT)ConsoleStartInfo->FillAttribute;
518 }
519 if (ConsoleStartInfo->dwStartupFlags & STARTF_USECOUNTCHARS)
520 {
521 ConsoleInfo->ScreenBufferSize = ConsoleStartInfo->ScreenBufferSize;
522 }
523 if (ConsoleStartInfo->dwStartupFlags & STARTF_USESIZE)
524 {
525 // ConsoleInfo->ConsoleSize = ConsoleStartInfo->ConsoleWindowSize;
526 ConsoleInfo->ConsoleSize.X = (SHORT)ConsoleStartInfo->ConsoleWindowSize.cx;
527 ConsoleInfo->ConsoleSize.Y = (SHORT)ConsoleStartInfo->ConsoleWindowSize.cy;
528 }
529 }
530 #endif
531
532 /*
533 * Fix the screen buffer size if needed. The rule is:
534 * ScreenBufferSize >= ConsoleSize
535 */
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;
540
541 /*
542 * Initialize the console
543 */
544 Console->State = CONSOLE_INITIALIZING;
545 Console->ReferenceCount = 0;
546 InitializeCriticalSection(&Console->Lock);
547 InitializeListHead(&Console->ProcessList);
548
549 /* Initialize the frontend interface */
550 ResetFrontEnd(Console);
551
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.
555
556 /*
557 * Initialize the input buffer
558 */
559 ConSrvInitObject(&Console->InputBuffer.Header, INPUT_BUFFER, Console);
560
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)
566 {
567 DeleteCriticalSection(&Console->Lock);
568 ConsoleFreeHeap(Console);
569 return STATUS_UNSUCCESSFUL;
570 }
571
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;
577
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;
583 // LineWakeupMask
584
585 // FIXME: This is terminal-specific !! VV
586 RtlZeroMemory(&Console->Selection, sizeof(CONSOLE_SELECTION_INFO));
587 Console->Selection.dwFlags = CONSOLE_NO_SELECTION;
588 // dwSelectionCursor
589
590 /* Set-up the code page */
591 Console->CodePage = Console->OutputCodePage = ConsoleInfo->CodePage;
592
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;
599
600 InitializeListHead(&Console->BufferList);
601 Status = ConDrvCreateScreenBuffer(&NewBuffer,
602 Console,
603 CONSOLE_TEXTMODE_BUFFER,
604 &ScreenBufferInfo);
605 if (!NT_SUCCESS(Status))
606 {
607 DPRINT1("ConDrvCreateScreenBuffer: failed, Status = 0x%08lx\n", Status);
608 CloseHandle(Console->InputBuffer.ActiveEvent);
609 DeleteCriticalSection(&Console->Lock);
610 ConsoleFreeHeap(Console);
611 return Status;
612 }
613 /* Make the new screen buffer active */
614 Console->ActiveBuffer = NewBuffer;
615 InitializeListHead(&Console->WriteWaitQueue);
616 Console->PauseFlags = 0;
617 Console->UnpauseEvent = NULL;
618
619 /*
620 * Initialize the alias and history buffers
621 */
622 Console->Aliases = NULL;
623 InitializeListHead(&Console->HistoryBuffers);
624 Console->HistoryBufferSize = ConsoleInfo->HistoryBufferSize;
625 Console->NumberOfHistoryBuffers = ConsoleInfo->NumberOfHistoryBuffers;
626 Console->HistoryNoDup = ConsoleInfo->HistoryNoDup;
627
628 /* Initialize the console title */
629 ConsoleCreateUnicodeString(&Console->OriginalTitle, ConsoleInfo->ConsoleTitle);
630 // if (ConsoleInfo.ConsoleTitle[0] == L'\0')
631 // {
632 // if (LoadStringW(ConSrvDllInstance, IDS_CONSOLE_TITLE, DefaultTitle, sizeof(DefaultTitle) / sizeof(DefaultTitle[0])))
633 // {
634 // ConsoleCreateUnicodeString(&Console->Title, DefaultTitle);
635 // }
636 // else
637 // {
638 // ConsoleCreateUnicodeString(&Console->Title, L"ReactOS Console");
639 // }
640 // }
641 // else
642 // {
643 ConsoleCreateUnicodeString(&Console->Title, ConsoleInfo->ConsoleTitle);
644 // }
645
646 /* Lock the console until its initialization is finished */
647 // EnterCriticalSection(&Console->Lock);
648
649 DPRINT("Console initialized\n");
650
651 /* All went right, so add the console to the list */
652 Status = InsertConsole(&ConsoleHandle, Console);
653 if (!NT_SUCCESS(Status))
654 {
655 /* Fail */
656 ConDrvDeleteConsole(Console);
657 return Status;
658 }
659
660 /* The initialization is finished */
661 DPRINT("Change state\n");
662 Console->State = CONSOLE_RUNNING;
663
664 /* Unlock the console */
665 // LeaveCriticalSection(&Console->Lock);
666
667 /* Return the newly created console to the caller and a success code too */
668 *NewConsoleHandle = ConsoleHandle;
669 *NewConsole = Console;
670 return STATUS_SUCCESS;
671 }
672
673 NTSTATUS NTAPI
674 ConDrvRegisterFrontEnd(IN PCONSOLE Console,
675 IN PFRONTEND FrontEnd)
676 {
677 NTSTATUS Status;
678
679 if (Console == NULL || FrontEnd == NULL)
680 return STATUS_INVALID_PARAMETER;
681
682 /* FIXME: Lock the console before ?? */
683
684 /*
685 * Attach the frontend to the console. Use now the TermIFace of the console,
686 * and not the user-defined temporary FrontEnd pointer.
687 */
688 Console->TermIFace = *FrontEnd;
689 Console->TermIFace.Console = Console;
690
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))
695 {
696 DPRINT1("FrontEnd initialization failed, Status = 0x%08lx\n", Status);
697
698 /* We failed, detach the frontend from the console */
699 FrontEnd->Console = NULL; // For the caller
700 ResetFrontEnd(Console);
701
702 return Status;
703 }
704
705 /* Copy buffer contents to screen */
706 // FrontEnd.Draw();
707 // ConioDrawConsole(Console);
708 DPRINT("Console drawn\n");
709
710 DPRINT("Terminal FrontEnd initialization done\n");
711 return STATUS_SUCCESS;
712 }
713
714 NTSTATUS NTAPI
715 ConDrvDeregisterFrontEnd(IN PCONSOLE Console)
716 {
717 if (Console == NULL) return STATUS_INVALID_PARAMETER;
718
719 /* FIXME: Lock the console before ?? */
720
721 /* Deinitialize the frontend BEFORE detaching it from the console */
722 Console->TermIFace.Vtbl->DeinitFrontEnd(&Console->TermIFace/*, Console*/);
723
724 /*
725 * Detach the frontend from the console:
726 * reinitialize the frontend interface.
727 */
728 ResetFrontEnd(Console);
729
730 DPRINT("Terminal FrontEnd unregistered\n");
731 return STATUS_SUCCESS;
732 }
733
734 VOID NTAPI
735 ConDrvDeleteConsole(IN PCONSOLE Console)
736 {
737 DPRINT("ConDrvDeleteConsole(0x%p)\n", Console);
738
739 /*
740 * Forbid validation of any console by other threads
741 * during the deletion of this console.
742 */
743 ConDrvLockConsoleListExclusive();
744
745 /*
746 * If the console is already being destroyed, i.e. not running
747 * or finishing to be initialized, just return.
748 */
749 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE) &&
750 !ConDrvValidateConsoleUnsafe(Console, CONSOLE_INITIALIZING, TRUE))
751 {
752 /* Unlock the console list and return */
753 ConDrvUnlockConsoleList();
754 return;
755 }
756
757 /*
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.
761 */
762 Console->State = CONSOLE_TERMINATING;
763
764 /*
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.
771 */
772 LeaveCriticalSection(&Console->Lock);
773 ConDrvUnlockConsoleList();
774
775 /* FIXME: Send a terminate message to all the processes owning this console */
776
777 /* Cleanup the UI-oriented part */
778 DPRINT("Deregister console\n");
779 ConDrvDeregisterFrontEnd(Console);
780 DPRINT("Console deregistered\n");
781
782 /***
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 ?).
786 ***/
787
788 ConDrvLockConsoleListExclusive();
789
790 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_TERMINATING, TRUE))
791 {
792 ConDrvUnlockConsoleList();
793 return;
794 }
795
796 /* We are now in destruction */
797 Console->State = CONSOLE_IN_DESTRUCTION;
798
799 /* We really delete the console. Reset the count to be sure. */
800 Console->ReferenceCount = 0;
801
802 /* Remove the console from the list */
803 RemoveConsoleByPointer(Console);
804
805 /* Discard all entries in the input event queue */
806 PurgeInputBuffer(Console);
807
808 if (Console->LineBuffer) ConsoleFreeHeap(Console->LineBuffer);
809
810 IntDeleteAllAliases(Console);
811 HistoryDeleteBuffers(Console);
812
813 ConioDeleteScreenBuffer(Console->ActiveBuffer);
814 Console->ActiveBuffer = NULL;
815 if (!IsListEmpty(&Console->BufferList))
816 {
817 DPRINT1("BUG: screen buffer list not empty\n");
818 ASSERT(FALSE);
819 }
820
821 /**/ CloseHandle(Console->InputBuffer.ActiveEvent); /**/
822 if (Console->UnpauseEvent) CloseHandle(Console->UnpauseEvent);
823
824 ConsoleFreeUnicodeString(&Console->OriginalTitle);
825 ConsoleFreeUnicodeString(&Console->Title);
826
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");
832
833 ConsoleFreeHeap(Console);
834 DPRINT("ConDrvDeleteConsole - Console destroyed\n");
835
836 /* Unlock the console list and return */
837 ConDrvUnlockConsoleList();
838 }
839
840
841 /* PUBLIC DRIVER APIS *********************************************************/
842
843 NTSTATUS NTAPI
844 ConDrvGetConsoleMode(IN PCONSOLE Console,
845 IN PCONSOLE_IO_OBJECT Object,
846 OUT PULONG ConsoleMode)
847 {
848 NTSTATUS Status = STATUS_SUCCESS;
849
850 if (Console == NULL || Object == NULL || ConsoleMode == NULL)
851 return STATUS_INVALID_PARAMETER;
852
853 /* Validity check */
854 ASSERT(Console == Object->Console);
855
856 /*** FIXME: */ *ConsoleMode = 0; /***/
857
858 if (INPUT_BUFFER == Object->Type)
859 {
860 PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
861
862 *ConsoleMode = InputBuffer->Mode;
863
864 if (Console->QuickEdit || Console->InsertMode)
865 {
866 // Windows does this, even if it's not documented on MSDN
867 *ConsoleMode |= ENABLE_EXTENDED_FLAGS;
868
869 if (Console->QuickEdit ) *ConsoleMode |= ENABLE_QUICK_EDIT_MODE;
870 if (Console->InsertMode) *ConsoleMode |= ENABLE_INSERT_MODE;
871 }
872 }
873 else if (TEXTMODE_BUFFER == Object->Type || GRAPHICS_BUFFER == Object->Type)
874 {
875 PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
876 *ConsoleMode = Buffer->Mode;
877 }
878 else
879 {
880 Status = STATUS_INVALID_HANDLE;
881 }
882
883 return Status;
884 }
885
886 NTSTATUS NTAPI
887 ConDrvSetConsoleMode(IN PCONSOLE Console,
888 IN PCONSOLE_IO_OBJECT Object,
889 IN ULONG ConsoleMode)
890 {
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 | \
894 ENABLE_MOUSE_INPUT )
895 #define CONSOLE_VALID_OUTPUT_MODES ( ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT )
896
897 NTSTATUS Status = STATUS_SUCCESS;
898
899 if (Console == NULL || Object == NULL)
900 return STATUS_INVALID_PARAMETER;
901
902 /* Validity check */
903 ASSERT(Console == Object->Console);
904
905 if (INPUT_BUFFER == Object->Type)
906 {
907 PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
908
909 DPRINT("SetConsoleMode(Input, %d)\n", ConsoleMode);
910
911 /*
912 * 1. Only the presence of valid mode flags is allowed.
913 */
914 if (ConsoleMode & ~(CONSOLE_VALID_INPUT_MODES | CONSOLE_VALID_CONTROL_MODES))
915 {
916 Status = STATUS_INVALID_PARAMETER;
917 goto Quit;
918 }
919
920 /*
921 * 2. If we use control mode flags without ENABLE_EXTENDED_FLAGS,
922 * then consider the flags invalid.
923 *
924 if ( (ConsoleMode & CONSOLE_VALID_CONTROL_MODES) &&
925 (ConsoleMode & ENABLE_EXTENDED_FLAGS) == 0 )
926 {
927 Status = STATUS_INVALID_PARAMETER;
928 goto Quit;
929 }
930 */
931
932 /*
933 * 3. Now we can continue.
934 */
935 if (ConsoleMode & CONSOLE_VALID_CONTROL_MODES)
936 {
937 Console->QuickEdit = !!(ConsoleMode & ENABLE_QUICK_EDIT_MODE);
938 Console->InsertMode = !!(ConsoleMode & ENABLE_INSERT_MODE);
939 }
940 InputBuffer->Mode = (ConsoleMode & CONSOLE_VALID_INPUT_MODES);
941 }
942 else if (TEXTMODE_BUFFER == Object->Type || GRAPHICS_BUFFER == Object->Type)
943 {
944 PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
945
946 DPRINT("SetConsoleMode(Output, %d)\n", ConsoleMode);
947
948 if (ConsoleMode & ~CONSOLE_VALID_OUTPUT_MODES)
949 {
950 Status = STATUS_INVALID_PARAMETER;
951 }
952 else
953 {
954 Buffer->Mode = (ConsoleMode & CONSOLE_VALID_OUTPUT_MODES);
955 }
956 }
957 else
958 {
959 Status = STATUS_INVALID_HANDLE;
960 }
961
962 Quit:
963 return Status;
964 }
965
966 NTSTATUS NTAPI
967 ConDrvGetConsoleTitle(IN PCONSOLE Console,
968 IN OUT PWCHAR Title,
969 IN OUT PULONG BufLength)
970 {
971 ULONG Length;
972
973 if (Console == NULL || Title == NULL || BufLength == NULL)
974 return STATUS_INVALID_PARAMETER;
975
976 /* Copy title of the console to the user title buffer */
977 if (*BufLength >= sizeof(WCHAR))
978 {
979 Length = min(*BufLength - sizeof(WCHAR), Console->Title.Length);
980 RtlCopyMemory(Title, Console->Title.Buffer, Length);
981 Title[Length / sizeof(WCHAR)] = L'\0';
982 }
983
984 *BufLength = Console->Title.Length;
985
986 return STATUS_SUCCESS;
987 }
988
989 NTSTATUS NTAPI
990 ConDrvSetConsoleTitle(IN PCONSOLE Console,
991 IN PWCHAR Title,
992 IN ULONG BufLength)
993 {
994 PWCHAR Buffer;
995
996 if (Console == NULL || Title == NULL)
997 return STATUS_INVALID_PARAMETER;
998
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;
1002
1003 /* Free the old title */
1004 ConsoleFreeUnicodeString(&Console->Title);
1005
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';
1012
1013 // ConioChangeTitle(Console);
1014 return STATUS_SUCCESS;
1015 }
1016
1017 NTSTATUS NTAPI
1018 ConDrvGetConsoleCP(IN PCONSOLE Console,
1019 OUT PUINT CodePage,
1020 IN BOOLEAN InputCP)
1021 {
1022 if (Console == NULL || CodePage == NULL)
1023 return STATUS_INVALID_PARAMETER;
1024
1025 *CodePage = (InputCP ? Console->CodePage : Console->OutputCodePage);
1026
1027 return STATUS_SUCCESS;
1028 }
1029
1030 NTSTATUS NTAPI
1031 ConDrvSetConsoleCP(IN PCONSOLE Console,
1032 IN UINT CodePage,
1033 IN BOOLEAN InputCP)
1034 {
1035 if (Console == NULL || !IsValidCodePage(CodePage))
1036 return STATUS_INVALID_PARAMETER;
1037
1038 if (InputCP)
1039 Console->CodePage = CodePage;
1040 else
1041 Console->OutputCodePage = CodePage;
1042
1043 return STATUS_SUCCESS;
1044 }
1045
1046 NTSTATUS NTAPI
1047 ConDrvGetConsoleProcessList(IN PCONSOLE Console,
1048 IN OUT PULONG ProcessIdsList,
1049 IN ULONG MaxIdListItems,
1050 OUT PULONG ProcessIdsTotal)
1051 {
1052 PCONSOLE_PROCESS_DATA current;
1053 PLIST_ENTRY current_entry;
1054
1055 if (Console == NULL || ProcessIdsList == NULL || ProcessIdsTotal == NULL)
1056 return STATUS_INVALID_PARAMETER;
1057
1058 *ProcessIdsTotal = 0;
1059
1060 for (current_entry = Console->ProcessList.Flink;
1061 current_entry != &Console->ProcessList;
1062 current_entry = current_entry->Flink)
1063 {
1064 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
1065 if (++(*ProcessIdsTotal) <= MaxIdListItems)
1066 {
1067 *ProcessIdsList++ = HandleToUlong(current->Process->ClientId.UniqueProcess);
1068 }
1069 }
1070
1071 return STATUS_SUCCESS;
1072 }
1073
1074 // ConDrvGenerateConsoleCtrlEvent
1075 NTSTATUS NTAPI
1076 ConDrvConsoleProcessCtrlEvent(IN PCONSOLE Console,
1077 IN ULONG ProcessGroupId,
1078 IN ULONG Event)
1079 {
1080 NTSTATUS Status = STATUS_SUCCESS;
1081 PLIST_ENTRY current_entry;
1082 PCONSOLE_PROCESS_DATA current;
1083
1084 /* If the console is already being destroyed, just return */
1085 if (!ConDrvValidateConsoleState(Console, CONSOLE_RUNNING))
1086 return STATUS_UNSUCCESSFUL;
1087
1088 /*
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).
1093 */
1094 current_entry = Console->ProcessList.Flink;
1095 while (current_entry != &Console->ProcessList)
1096 {
1097 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
1098 current_entry = current_entry->Flink;
1099
1100 /*
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.
1103 */
1104 if (ProcessGroupId == 0 || current->Process->ProcessGroupId == ProcessGroupId)
1105 {
1106 Status = ConDrvConsoleCtrlEvent(Event, current);
1107 }
1108 }
1109
1110 return Status;
1111 }
1112
1113 /* EOF */