[CONSRV-WINSRV]
[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 ULONG Index = HandleToULong(Handle) >> 2;
147 PCONSOLE Console;
148
149 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
150 (ConsoleList != NULL && ConsoleListSize != 0) );
151
152 /* Remove the console from the list */
153 ConDrvLockConsoleListExclusive();
154
155 if (Index >= ConsoleListSize ||
156 (Console = ConsoleList[Index]) == NULL)
157 {
158 Status = STATUS_INVALID_HANDLE;
159 goto Quit;
160 }
161
162 ConsoleList[Index] = NULL;
163
164 Quit:
165 /* Unlock the console list and return status */
166 ConDrvUnlockConsoleList();
167 return Status;
168 }
169 #endif
170
171 static NTSTATUS
172 RemoveConsoleByPointer(IN PCONSOLE Console)
173 {
174 ULONG i = 0;
175
176 if (!Console) return STATUS_INVALID_PARAMETER;
177
178 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
179 (ConsoleList != NULL && ConsoleListSize != 0) );
180
181 /* Remove the console from the list */
182 ConDrvLockConsoleListExclusive();
183
184 if (ConsoleList)
185 {
186 for (i = 0; i < ConsoleListSize; i++)
187 {
188 if (ConsoleList[i] == Console) ConsoleList[i] = NULL;
189 }
190 }
191
192 /* Unlock the console list */
193 ConDrvUnlockConsoleList();
194
195 return STATUS_SUCCESS;
196 }
197
198
199 /* For resetting the frontend - defined in dummyfrontend.c */
200 VOID ResetFrontEnd(IN PCONSOLE Console);
201
202
203 /* PRIVATE FUNCTIONS **********************************************************/
204
205 static NTSTATUS
206 ConDrvConsoleCtrlEventTimeout(IN ULONG Event,
207 IN PCONSOLE_PROCESS_DATA ProcessData,
208 IN ULONG Timeout)
209 {
210 NTSTATUS Status = STATUS_SUCCESS;
211
212 DPRINT("ConDrvConsoleCtrlEventTimeout Parent ProcessId = %x\n", ProcessData->Process->ClientId.UniqueProcess);
213
214 if (ProcessData->CtrlDispatcher)
215 {
216 _SEH2_TRY
217 {
218 HANDLE Thread = NULL;
219
220 _SEH2_TRY
221 {
222 Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0,
223 ProcessData->CtrlDispatcher,
224 UlongToPtr(Event), 0, NULL);
225 if (NULL == Thread)
226 {
227 Status = RtlGetLastNtStatus();
228 DPRINT1("Failed thread creation, Status = 0x%08lx\n", Status);
229 }
230 else
231 {
232 DPRINT("ProcessData->CtrlDispatcher remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n", ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process);
233 WaitForSingleObject(Thread, Timeout);
234 }
235 }
236 _SEH2_FINALLY
237 {
238 CloseHandle(Thread);
239 }
240 _SEH2_END;
241 }
242 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
243 {
244 Status = _SEH2_GetExceptionCode();
245 DPRINT1("ConDrvConsoleCtrlEventTimeout - Caught an exception, Status = 0x%08lx\n", Status);
246 }
247 _SEH2_END;
248 }
249
250 return Status;
251 }
252
253 static NTSTATUS
254 ConDrvConsoleCtrlEvent(IN ULONG Event,
255 IN PCONSOLE_PROCESS_DATA ProcessData)
256 {
257 return ConDrvConsoleCtrlEventTimeout(Event, ProcessData, 0);
258 }
259
260 VOID FASTCALL
261 ConioPause(PCONSOLE Console, UINT Flags)
262 {
263 Console->PauseFlags |= Flags;
264 if (!Console->UnpauseEvent)
265 Console->UnpauseEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
266 }
267
268 VOID FASTCALL
269 ConioUnpause(PCONSOLE Console, UINT Flags)
270 {
271 Console->PauseFlags &= ~Flags;
272
273 // if ((Console->PauseFlags & (PAUSED_FROM_KEYBOARD | PAUSED_FROM_SCROLLBAR | PAUSED_FROM_SELECTION)) == 0)
274 if (Console->PauseFlags == 0 && Console->UnpauseEvent)
275 {
276 SetEvent(Console->UnpauseEvent);
277 CloseHandle(Console->UnpauseEvent);
278 Console->UnpauseEvent = NULL;
279
280 CsrNotifyWait(&Console->WriteWaitQueue,
281 WaitAll,
282 NULL,
283 NULL);
284 if (!IsListEmpty(&Console->WriteWaitQueue))
285 {
286 CsrDereferenceWait(&Console->WriteWaitQueue);
287 }
288 }
289 }
290
291
292 /*
293 * Console accessibility check helpers
294 */
295
296 BOOLEAN NTAPI
297 ConDrvValidateConsoleState(IN PCONSOLE Console,
298 IN CONSOLE_STATE ExpectedState)
299 {
300 // if (!Console) return FALSE;
301
302 /* The console must be locked */
303 // ASSERT(Console_locked);
304
305 return (Console->State == ExpectedState);
306 }
307
308 BOOLEAN NTAPI
309 ConDrvValidateConsoleUnsafe(IN PCONSOLE Console,
310 IN CONSOLE_STATE ExpectedState,
311 IN BOOLEAN LockConsole)
312 {
313 if (!Console) return FALSE;
314
315 /*
316 * Lock the console to forbid possible console's state changes
317 * (which must be done when the console is already locked).
318 * If we don't want to lock it, it's because the lock is already
319 * held. So there must be no problems.
320 */
321 if (LockConsole) EnterCriticalSection(&Console->Lock);
322
323 // ASSERT(Console_locked);
324
325 /* Check whether the console's state is what we expect */
326 if (!ConDrvValidateConsoleState(Console, ExpectedState))
327 {
328 if (LockConsole) LeaveCriticalSection(&Console->Lock);
329 return FALSE;
330 }
331
332 return TRUE;
333 }
334
335 BOOLEAN NTAPI
336 ConDrvValidateConsole(OUT PCONSOLE* Console,
337 IN HANDLE ConsoleHandle,
338 IN CONSOLE_STATE ExpectedState,
339 IN BOOLEAN LockConsole)
340 {
341 BOOLEAN RetVal = FALSE;
342
343 ULONG Index = HandleToULong(ConsoleHandle) >> 2;
344 PCONSOLE ValidatedConsole;
345
346 if (!Console) return FALSE;
347 *Console = NULL;
348
349 /*
350 * Forbid creation or deletion of consoles when
351 * checking for the existence of a console.
352 */
353 ConDrvLockConsoleListShared();
354
355 if (Index >= ConsoleListSize ||
356 (ValidatedConsole = ConsoleList[Index]) == NULL)
357 {
358 /* Unlock the console list */
359 ConDrvUnlockConsoleList();
360
361 return FALSE;
362 }
363
364 ValidatedConsole = ConsoleList[Index];
365
366 /* Unlock the console list and return */
367 ConDrvUnlockConsoleList();
368
369 RetVal = ConDrvValidateConsoleUnsafe(ValidatedConsole,
370 ExpectedState,
371 LockConsole);
372 if (RetVal) *Console = ValidatedConsole;
373
374 return RetVal;
375 }
376
377 NTSTATUS NTAPI
378 ConDrvGetConsole(OUT PCONSOLE* Console,
379 IN HANDLE ConsoleHandle,
380 IN BOOLEAN LockConsole)
381 {
382 NTSTATUS Status = STATUS_INVALID_HANDLE;
383 PCONSOLE GrabConsole;
384
385 if (Console == NULL) return STATUS_INVALID_PARAMETER;
386 *Console = NULL;
387
388 if (ConDrvValidateConsole(&GrabConsole,
389 ConsoleHandle,
390 CONSOLE_RUNNING,
391 LockConsole))
392 {
393 InterlockedIncrement(&GrabConsole->ReferenceCount);
394 *Console = GrabConsole;
395 Status = STATUS_SUCCESS;
396 }
397
398 return Status;
399 }
400
401 VOID NTAPI
402 ConDrvReleaseConsole(IN PCONSOLE Console,
403 IN BOOLEAN WasConsoleLocked)
404 {
405 LONG RefCount = 0;
406
407 if (!Console) return;
408 // if (Console->ReferenceCount == 0) return; // This shouldn't happen
409 ASSERT(Console->ReferenceCount > 0);
410
411 /* The console must be locked */
412 // ASSERT(Console_locked);
413
414 /*
415 * Decrement the reference count. Save the new value too,
416 * because Console->ReferenceCount might be modified after
417 * the console gets unlocked but before we check whether we
418 * can destroy it.
419 */
420 RefCount = _InterlockedDecrement(&Console->ReferenceCount);
421
422 /* Unlock the console if needed */
423 if (WasConsoleLocked) LeaveCriticalSection(&Console->Lock);
424
425 /* Delete the console if needed */
426 if (RefCount <= 0) ConDrvDeleteConsole(Console);
427 }
428
429
430 /* CONSOLE INITIALIZATION FUNCTIONS *******************************************/
431
432 VOID NTAPI
433 ConDrvInitConsoleSupport(VOID)
434 {
435 DPRINT("CONSRV: ConDrvInitConsoleSupport()\n");
436
437 /* Initialize the console list and its lock */
438 ConsoleListSize = 0;
439 ConsoleList = NULL;
440 RtlInitializeResource(&ListLock);
441
442 /* Should call LoadKeyboardLayout */
443 }
444
445 NTSTATUS NTAPI
446 ConDrvInitConsole(OUT PHANDLE NewConsoleHandle,
447 OUT PCONSOLE* NewConsole,
448 IN PCONSOLE_INFO ConsoleInfo,
449 IN ULONG ConsoleLeaderProcessId)
450 {
451 NTSTATUS Status;
452 SECURITY_ATTRIBUTES SecurityAttributes;
453 // CONSOLE_INFO CapturedConsoleInfo;
454 TEXTMODE_BUFFER_INFO ScreenBufferInfo;
455 HANDLE ConsoleHandle;
456 PCONSOLE Console;
457 PCONSOLE_SCREEN_BUFFER NewBuffer;
458 // WCHAR DefaultTitle[128];
459
460 if (NewConsoleHandle == NULL || NewConsole == NULL || ConsoleInfo == NULL)
461 return STATUS_INVALID_PARAMETER;
462
463 *NewConsoleHandle = NULL;
464 *NewConsole = NULL;
465
466 /*
467 * Allocate a console structure
468 */
469 Console = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(CONSOLE));
470 if (NULL == Console)
471 {
472 DPRINT1("Not enough memory for console creation.\n");
473 return STATUS_NO_MEMORY;
474 }
475
476 /*
477 * Load the console settings
478 */
479
480 /* 1. Load the default settings */
481 // ConSrvGetDefaultSettings(ConsoleInfo, ProcessId);
482
483 // /* 2. Get the title of the console (initialize ConsoleInfo.ConsoleTitle) */
484 // Length = min(wcslen(ConsoleStartInfo->ConsoleTitle),
485 // sizeof(ConsoleInfo.ConsoleTitle) / sizeof(ConsoleInfo.ConsoleTitle[0]) - 1);
486 // wcsncpy(ConsoleInfo.ConsoleTitle, ConsoleStartInfo->ConsoleTitle, Length);
487 // ConsoleInfo.ConsoleTitle[Length] = L'\0';
488
489 /*
490 * 4. Load the remaining console settings via the registry.
491 */
492 #if 0
493 if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
494 {
495 /*
496 * Either we weren't created by an app launched via a shell-link,
497 * or we failed to load shell-link console properties.
498 * Therefore, load the console infos for the application from the registry.
499 */
500 ConSrvReadUserSettings(ConsoleInfo, ProcessId);
501
502 /*
503 * Now, update them with the properties the user might gave to us
504 * via the STARTUPINFO structure before calling CreateProcess
505 * (and which was transmitted via the ConsoleStartInfo structure).
506 * We therefore overwrite the values read in the registry.
507 */
508 if (ConsoleStartInfo->dwStartupFlags & STARTF_USEFILLATTRIBUTE)
509 {
510 ConsoleInfo->ScreenAttrib = (USHORT)ConsoleStartInfo->FillAttribute;
511 }
512 if (ConsoleStartInfo->dwStartupFlags & STARTF_USECOUNTCHARS)
513 {
514 ConsoleInfo->ScreenBufferSize = ConsoleStartInfo->ScreenBufferSize;
515 }
516 if (ConsoleStartInfo->dwStartupFlags & STARTF_USESIZE)
517 {
518 // ConsoleInfo->ConsoleSize = ConsoleStartInfo->ConsoleWindowSize;
519 ConsoleInfo->ConsoleSize.X = (SHORT)ConsoleStartInfo->ConsoleWindowSize.cx;
520 ConsoleInfo->ConsoleSize.Y = (SHORT)ConsoleStartInfo->ConsoleWindowSize.cy;
521 }
522 }
523 #endif
524
525 /*
526 * Fix the screen buffer size if needed. The rule is:
527 * ScreenBufferSize >= ConsoleSize
528 */
529 if (ConsoleInfo->ScreenBufferSize.X < ConsoleInfo->ConsoleSize.X)
530 ConsoleInfo->ScreenBufferSize.X = ConsoleInfo->ConsoleSize.X;
531 if (ConsoleInfo->ScreenBufferSize.Y < ConsoleInfo->ConsoleSize.Y)
532 ConsoleInfo->ScreenBufferSize.Y = ConsoleInfo->ConsoleSize.Y;
533
534 /*
535 * Initialize the console
536 */
537 Console->State = CONSOLE_INITIALIZING;
538 Console->ReferenceCount = 0;
539 InitializeCriticalSection(&Console->Lock);
540 InitializeListHead(&Console->ProcessList);
541
542 /* Initialize the frontend interface */
543 ResetFrontEnd(Console);
544
545 memcpy(Console->Colors, ConsoleInfo->Colors, sizeof(ConsoleInfo->Colors));
546 Console->ConsoleSize = ConsoleInfo->ConsoleSize;
547 Console->FixedSize = FALSE; // Value by default; is reseted by the front-ends if needed.
548
549 /*
550 * Initialize the input buffer
551 */
552 ConSrvInitObject(&Console->InputBuffer.Header, INPUT_BUFFER, Console);
553
554 SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
555 SecurityAttributes.lpSecurityDescriptor = NULL;
556 SecurityAttributes.bInheritHandle = TRUE;
557 Console->InputBuffer.ActiveEvent = CreateEventW(&SecurityAttributes, TRUE, FALSE, NULL);
558 if (NULL == Console->InputBuffer.ActiveEvent)
559 {
560 DeleteCriticalSection(&Console->Lock);
561 ConsoleFreeHeap(Console);
562 return STATUS_UNSUCCESSFUL;
563 }
564
565 Console->InputBuffer.InputBufferSize = 0; // FIXME!
566 InitializeListHead(&Console->InputBuffer.InputEvents);
567 InitializeListHead(&Console->InputBuffer.ReadWaitQueue);
568 Console->InputBuffer.Mode = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
569 ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT;
570
571 Console->QuickEdit = ConsoleInfo->QuickEdit;
572 Console->InsertMode = ConsoleInfo->InsertMode;
573 Console->LineBuffer = NULL;
574 Console->LineMaxSize = Console->LineSize = Console->LinePos = 0;
575 Console->LineComplete = Console->LineUpPressed = Console->LineInsertToggle = FALSE;
576 // LineWakeupMask
577
578 // FIXME: This is terminal-specific !! VV
579 RtlZeroMemory(&Console->Selection, sizeof(CONSOLE_SELECTION_INFO));
580 Console->Selection.dwFlags = CONSOLE_NO_SELECTION;
581 // dwSelectionCursor
582
583 /* Set-up the code page */
584 Console->CodePage = Console->OutputCodePage = ConsoleInfo->CodePage;
585
586 /* Initialize a new text-mode screen buffer with default settings */
587 ScreenBufferInfo.ScreenBufferSize = ConsoleInfo->ScreenBufferSize;
588 ScreenBufferInfo.ScreenAttrib = ConsoleInfo->ScreenAttrib;
589 ScreenBufferInfo.PopupAttrib = ConsoleInfo->PopupAttrib;
590 ScreenBufferInfo.IsCursorVisible = TRUE;
591 ScreenBufferInfo.CursorSize = ConsoleInfo->CursorSize;
592
593 InitializeListHead(&Console->BufferList);
594 Status = ConDrvCreateScreenBuffer(&NewBuffer,
595 Console,
596 CONSOLE_TEXTMODE_BUFFER,
597 &ScreenBufferInfo);
598 if (!NT_SUCCESS(Status))
599 {
600 DPRINT1("ConDrvCreateScreenBuffer: failed, Status = 0x%08lx\n", Status);
601 CloseHandle(Console->InputBuffer.ActiveEvent);
602 DeleteCriticalSection(&Console->Lock);
603 ConsoleFreeHeap(Console);
604 return Status;
605 }
606 /* Make the new screen buffer active */
607 Console->ActiveBuffer = NewBuffer;
608 InitializeListHead(&Console->WriteWaitQueue);
609 Console->PauseFlags = 0;
610 Console->UnpauseEvent = NULL;
611
612 /*
613 * Initialize the alias and history buffers
614 */
615 Console->Aliases = NULL;
616 InitializeListHead(&Console->HistoryBuffers);
617 Console->HistoryBufferSize = ConsoleInfo->HistoryBufferSize;
618 Console->NumberOfHistoryBuffers = ConsoleInfo->NumberOfHistoryBuffers;
619 Console->HistoryNoDup = ConsoleInfo->HistoryNoDup;
620
621 /* Initialize the console title */
622 ConsoleCreateUnicodeString(&Console->OriginalTitle, ConsoleInfo->ConsoleTitle);
623 // if (ConsoleInfo.ConsoleTitle[0] == L'\0')
624 // {
625 // if (LoadStringW(ConSrvDllInstance, IDS_CONSOLE_TITLE, DefaultTitle, sizeof(DefaultTitle) / sizeof(DefaultTitle[0])))
626 // {
627 // ConsoleCreateUnicodeString(&Console->Title, DefaultTitle);
628 // }
629 // else
630 // {
631 // ConsoleCreateUnicodeString(&Console->Title, L"ReactOS Console");
632 // }
633 // }
634 // else
635 // {
636 ConsoleCreateUnicodeString(&Console->Title, ConsoleInfo->ConsoleTitle);
637 // }
638
639 /* Lock the console until its initialization is finished */
640 // EnterCriticalSection(&Console->Lock);
641
642 DPRINT("Console initialized\n");
643
644 /* All went right, so add the console to the list */
645 Status = InsertConsole(&ConsoleHandle, Console);
646 if (!NT_SUCCESS(Status))
647 {
648 /* Fail */
649 ConDrvDeleteConsole(Console);
650 return Status;
651 }
652
653 /* The initialization is finished */
654 DPRINT("Change state\n");
655 Console->State = CONSOLE_RUNNING;
656
657 /* Unlock the console */
658 // LeaveCriticalSection(&Console->Lock);
659
660 /* Return the newly created console to the caller and a success code too */
661 *NewConsoleHandle = ConsoleHandle;
662 *NewConsole = Console;
663 return STATUS_SUCCESS;
664 }
665
666 NTSTATUS NTAPI
667 ConDrvRegisterFrontEnd(IN PCONSOLE Console,
668 IN PFRONTEND FrontEnd)
669 {
670 NTSTATUS Status;
671
672 if (Console == NULL || FrontEnd == NULL)
673 return STATUS_INVALID_PARAMETER;
674
675 /* FIXME: Lock the console before ?? */
676
677 /*
678 * Attach the frontend to the console. Use now the TermIFace of the console,
679 * and not the user-defined temporary FrontEnd pointer.
680 */
681 Console->TermIFace = *FrontEnd;
682 Console->TermIFace.Console = Console;
683
684 /* Initialize the frontend AFTER having attached it to the console */
685 DPRINT("Finish initialization of frontend\n");
686 Status = Console->TermIFace.Vtbl->InitFrontEnd(&Console->TermIFace, Console);
687 if (!NT_SUCCESS(Status))
688 {
689 DPRINT1("FrontEnd initialization failed, Status = 0x%08lx\n", Status);
690
691 /* We failed, detach the frontend from the console */
692 FrontEnd->Console = NULL; // For the caller
693 ResetFrontEnd(Console);
694
695 return Status;
696 }
697
698 /* Copy buffer contents to screen */
699 // FrontEnd.Draw();
700 // ConioDrawConsole(Console);
701 DPRINT("Console drawn\n");
702
703 DPRINT("Terminal FrontEnd initialization done\n");
704 return STATUS_SUCCESS;
705 }
706
707 NTSTATUS NTAPI
708 ConDrvDeregisterFrontEnd(IN PCONSOLE Console)
709 {
710 if (Console == NULL) return STATUS_INVALID_PARAMETER;
711
712 /* FIXME: Lock the console before ?? */
713
714 /* Deinitialize the frontend BEFORE detaching it from the console */
715 Console->TermIFace.Vtbl->DeinitFrontEnd(&Console->TermIFace/*, Console*/);
716
717 /*
718 * Detach the frontend from the console:
719 * reinitialize the frontend interface.
720 */
721 ResetFrontEnd(Console);
722
723 DPRINT("Terminal FrontEnd unregistered\n");
724 return STATUS_SUCCESS;
725 }
726
727 VOID NTAPI
728 ConDrvDeleteConsole(IN PCONSOLE Console)
729 {
730 DPRINT("ConDrvDeleteConsole(0x%p)\n", Console);
731
732 /*
733 * Forbid validation of any console by other threads
734 * during the deletion of this console.
735 */
736 ConDrvLockConsoleListExclusive();
737
738 /*
739 * If the console is already being destroyed, i.e. not running
740 * or finishing to be initialized, just return.
741 */
742 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE) &&
743 !ConDrvValidateConsoleUnsafe(Console, CONSOLE_INITIALIZING, TRUE))
744 {
745 /* Unlock the console list and return */
746 ConDrvUnlockConsoleList();
747 return;
748 }
749
750 /*
751 * We are about to be destroyed. Signal it to other people
752 * so that they can terminate what they are doing, and that
753 * they cannot longer validate the console.
754 */
755 Console->State = CONSOLE_TERMINATING;
756
757 /*
758 * Allow other threads to finish their job: basically, unlock
759 * all other calls to EnterCriticalSection(&Console->Lock); by
760 * ConDrvValidateConsole(Unsafe) functions so that they just see
761 * that we are not in CONSOLE_RUNNING state anymore, or unlock
762 * other concurrent calls to ConDrvDeleteConsole so that they
763 * can see that we are in fact already deleting the console.
764 */
765 LeaveCriticalSection(&Console->Lock);
766 ConDrvUnlockConsoleList();
767
768 /* FIXME: Send a terminate message to all the processes owning this console */
769
770 /* Cleanup the UI-oriented part */
771 DPRINT("Deregister console\n");
772 ConDrvDeregisterFrontEnd(Console);
773 DPRINT("Console deregistered\n");
774
775 /***
776 * Check that the console is in terminating state before continuing
777 * (the cleanup code must not change the state of the console...
778 * ...unless to cancel console deletion ?).
779 ***/
780
781 ConDrvLockConsoleListExclusive();
782
783 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_TERMINATING, TRUE))
784 {
785 ConDrvUnlockConsoleList();
786 return;
787 }
788
789 /* We are now in destruction */
790 Console->State = CONSOLE_IN_DESTRUCTION;
791
792 /* We really delete the console. Reset the count to be sure. */
793 Console->ReferenceCount = 0;
794
795 /* Remove the console from the list */
796 RemoveConsoleByPointer(Console);
797
798 /* Discard all entries in the input event queue */
799 PurgeInputBuffer(Console);
800
801 if (Console->LineBuffer) ConsoleFreeHeap(Console->LineBuffer);
802
803 IntDeleteAllAliases(Console);
804 HistoryDeleteBuffers(Console);
805
806 ConioDeleteScreenBuffer(Console->ActiveBuffer);
807 Console->ActiveBuffer = NULL;
808 if (!IsListEmpty(&Console->BufferList))
809 {
810 DPRINT1("BUG: screen buffer list not empty\n");
811 ASSERT(FALSE);
812 }
813
814 /**/ CloseHandle(Console->InputBuffer.ActiveEvent); /**/
815 if (Console->UnpauseEvent) CloseHandle(Console->UnpauseEvent);
816
817 ConsoleFreeUnicodeString(&Console->OriginalTitle);
818 ConsoleFreeUnicodeString(&Console->Title);
819
820 DPRINT("ConDrvDeleteConsole - Unlocking\n");
821 LeaveCriticalSection(&Console->Lock);
822 DPRINT("ConDrvDeleteConsole - Destroying lock\n");
823 DeleteCriticalSection(&Console->Lock);
824 DPRINT("ConDrvDeleteConsole - Lock destroyed ; freeing console\n");
825
826 ConsoleFreeHeap(Console);
827 DPRINT("ConDrvDeleteConsole - Console destroyed\n");
828
829 /* Unlock the console list and return */
830 ConDrvUnlockConsoleList();
831 }
832
833
834 /* PUBLIC DRIVER APIS *********************************************************/
835
836 NTSTATUS NTAPI
837 ConDrvGetConsoleMode(IN PCONSOLE Console,
838 IN PCONSOLE_IO_OBJECT Object,
839 OUT PULONG ConsoleMode)
840 {
841 NTSTATUS Status = STATUS_SUCCESS;
842
843 if (Console == NULL || Object == NULL || ConsoleMode == NULL)
844 return STATUS_INVALID_PARAMETER;
845
846 /* Validity check */
847 ASSERT(Console == Object->Console);
848
849 /*** FIXME: */ *ConsoleMode = 0; /***/
850
851 if (INPUT_BUFFER == Object->Type)
852 {
853 PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
854
855 *ConsoleMode = InputBuffer->Mode;
856
857 if (Console->QuickEdit || Console->InsertMode)
858 {
859 // Windows does this, even if it's not documented on MSDN
860 *ConsoleMode |= ENABLE_EXTENDED_FLAGS;
861
862 if (Console->QuickEdit ) *ConsoleMode |= ENABLE_QUICK_EDIT_MODE;
863 if (Console->InsertMode) *ConsoleMode |= ENABLE_INSERT_MODE;
864 }
865 }
866 else if (TEXTMODE_BUFFER == Object->Type || GRAPHICS_BUFFER == Object->Type)
867 {
868 PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
869 *ConsoleMode = Buffer->Mode;
870 }
871 else
872 {
873 Status = STATUS_INVALID_HANDLE;
874 }
875
876 return Status;
877 }
878
879 NTSTATUS NTAPI
880 ConDrvSetConsoleMode(IN PCONSOLE Console,
881 IN PCONSOLE_IO_OBJECT Object,
882 IN ULONG ConsoleMode)
883 {
884 #define CONSOLE_VALID_CONTROL_MODES ( ENABLE_EXTENDED_FLAGS | ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE )
885 #define CONSOLE_VALID_INPUT_MODES ( ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | \
886 ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT | \
887 ENABLE_MOUSE_INPUT )
888 #define CONSOLE_VALID_OUTPUT_MODES ( ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT )
889
890 NTSTATUS Status = STATUS_SUCCESS;
891
892 if (Console == NULL || Object == NULL)
893 return STATUS_INVALID_PARAMETER;
894
895 /* Validity check */
896 ASSERT(Console == Object->Console);
897
898 if (INPUT_BUFFER == Object->Type)
899 {
900 PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
901
902 DPRINT("SetConsoleMode(Input, %d)\n", ConsoleMode);
903
904 /*
905 * 1. Only the presence of valid mode flags is allowed.
906 */
907 if (ConsoleMode & ~(CONSOLE_VALID_INPUT_MODES | CONSOLE_VALID_CONTROL_MODES))
908 {
909 Status = STATUS_INVALID_PARAMETER;
910 goto Quit;
911 }
912
913 /*
914 * 2. If we use control mode flags without ENABLE_EXTENDED_FLAGS,
915 * then consider the flags invalid.
916 *
917 if ( (ConsoleMode & CONSOLE_VALID_CONTROL_MODES) &&
918 (ConsoleMode & ENABLE_EXTENDED_FLAGS) == 0 )
919 {
920 Status = STATUS_INVALID_PARAMETER;
921 goto Quit;
922 }
923 */
924
925 /*
926 * 3. Now we can continue.
927 */
928 if (ConsoleMode & CONSOLE_VALID_CONTROL_MODES)
929 {
930 Console->QuickEdit = !!(ConsoleMode & ENABLE_QUICK_EDIT_MODE);
931 Console->InsertMode = !!(ConsoleMode & ENABLE_INSERT_MODE);
932 }
933 InputBuffer->Mode = (ConsoleMode & CONSOLE_VALID_INPUT_MODES);
934 }
935 else if (TEXTMODE_BUFFER == Object->Type || GRAPHICS_BUFFER == Object->Type)
936 {
937 PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
938
939 DPRINT("SetConsoleMode(Output, %d)\n", ConsoleMode);
940
941 if (ConsoleMode & ~CONSOLE_VALID_OUTPUT_MODES)
942 {
943 Status = STATUS_INVALID_PARAMETER;
944 }
945 else
946 {
947 Buffer->Mode = (ConsoleMode & CONSOLE_VALID_OUTPUT_MODES);
948 }
949 }
950 else
951 {
952 Status = STATUS_INVALID_HANDLE;
953 }
954
955 Quit:
956 return Status;
957 }
958
959 NTSTATUS NTAPI
960 ConDrvGetConsoleTitle(IN PCONSOLE Console,
961 IN OUT PWCHAR Title,
962 IN OUT PULONG BufLength)
963 {
964 ULONG Length;
965
966 if (Console == NULL || Title == NULL || BufLength == NULL)
967 return STATUS_INVALID_PARAMETER;
968
969 /* Copy title of the console to the user title buffer */
970 if (*BufLength >= sizeof(WCHAR))
971 {
972 Length = min(*BufLength - sizeof(WCHAR), Console->Title.Length);
973 RtlCopyMemory(Title, Console->Title.Buffer, Length);
974 Title[Length / sizeof(WCHAR)] = L'\0';
975 }
976
977 *BufLength = Console->Title.Length;
978
979 return STATUS_SUCCESS;
980 }
981
982 NTSTATUS NTAPI
983 ConDrvSetConsoleTitle(IN PCONSOLE Console,
984 IN PWCHAR Title,
985 IN ULONG BufLength)
986 {
987 PWCHAR Buffer;
988
989 if (Console == NULL || Title == NULL)
990 return STATUS_INVALID_PARAMETER;
991
992 /* Allocate a new buffer to hold the new title (NULL-terminated) */
993 Buffer = ConsoleAllocHeap(0, BufLength + sizeof(WCHAR));
994 if (!Buffer) return STATUS_NO_MEMORY;
995
996 /* Free the old title */
997 ConsoleFreeUnicodeString(&Console->Title);
998
999 /* Copy title to console */
1000 Console->Title.Buffer = Buffer;
1001 Console->Title.Length = BufLength;
1002 Console->Title.MaximumLength = Console->Title.Length + sizeof(WCHAR);
1003 RtlCopyMemory(Console->Title.Buffer, Title, Console->Title.Length);
1004 Console->Title.Buffer[Console->Title.Length / sizeof(WCHAR)] = L'\0';
1005
1006 // ConioChangeTitle(Console);
1007 return STATUS_SUCCESS;
1008 }
1009
1010 NTSTATUS NTAPI
1011 ConDrvGetConsoleCP(IN PCONSOLE Console,
1012 OUT PUINT CodePage,
1013 IN BOOLEAN InputCP)
1014 {
1015 if (Console == NULL || CodePage == NULL)
1016 return STATUS_INVALID_PARAMETER;
1017
1018 *CodePage = (InputCP ? Console->CodePage : Console->OutputCodePage);
1019
1020 return STATUS_SUCCESS;
1021 }
1022
1023 NTSTATUS NTAPI
1024 ConDrvSetConsoleCP(IN PCONSOLE Console,
1025 IN UINT CodePage,
1026 IN BOOLEAN InputCP)
1027 {
1028 if (Console == NULL || !IsValidCodePage(CodePage))
1029 return STATUS_INVALID_PARAMETER;
1030
1031 if (InputCP)
1032 Console->CodePage = CodePage;
1033 else
1034 Console->OutputCodePage = CodePage;
1035
1036 return STATUS_SUCCESS;
1037 }
1038
1039 NTSTATUS NTAPI
1040 ConDrvGetConsoleProcessList(IN PCONSOLE Console,
1041 IN OUT PULONG ProcessIdsList,
1042 IN ULONG MaxIdListItems,
1043 OUT PULONG ProcessIdsTotal)
1044 {
1045 PCONSOLE_PROCESS_DATA current;
1046 PLIST_ENTRY current_entry;
1047
1048 if (Console == NULL || ProcessIdsList == NULL || ProcessIdsTotal == NULL)
1049 return STATUS_INVALID_PARAMETER;
1050
1051 *ProcessIdsTotal = 0;
1052
1053 for (current_entry = Console->ProcessList.Flink;
1054 current_entry != &Console->ProcessList;
1055 current_entry = current_entry->Flink)
1056 {
1057 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
1058 if (++(*ProcessIdsTotal) <= MaxIdListItems)
1059 {
1060 *ProcessIdsList++ = HandleToUlong(current->Process->ClientId.UniqueProcess);
1061 }
1062 }
1063
1064 return STATUS_SUCCESS;
1065 }
1066
1067 // ConDrvGenerateConsoleCtrlEvent
1068 NTSTATUS NTAPI
1069 ConDrvConsoleProcessCtrlEvent(IN PCONSOLE Console,
1070 IN ULONG ProcessGroupId,
1071 IN ULONG Event)
1072 {
1073 NTSTATUS Status = STATUS_SUCCESS;
1074 PLIST_ENTRY current_entry;
1075 PCONSOLE_PROCESS_DATA current;
1076
1077 /* If the console is already being destroyed, just return */
1078 if (!ConDrvValidateConsoleState(Console, CONSOLE_RUNNING))
1079 return STATUS_UNSUCCESSFUL;
1080
1081 /*
1082 * Loop through the process list, from the most recent process
1083 * (the active one) to the oldest one (the first created, i.e.
1084 * the console leader process), and for each, send an event
1085 * (new processes are inserted at the head of the console process list).
1086 */
1087 current_entry = Console->ProcessList.Flink;
1088 while (current_entry != &Console->ProcessList)
1089 {
1090 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
1091 current_entry = current_entry->Flink;
1092
1093 /*
1094 * Only processes belonging to the same process group are signaled.
1095 * If the process group ID is zero, then all the processes are signaled.
1096 */
1097 if (ProcessGroupId == 0 || current->Process->ProcessGroupId == ProcessGroupId)
1098 {
1099 Status = ConDrvConsoleCtrlEvent(Event, current);
1100 }
1101 }
1102
1103 return Status;
1104 }
1105
1106 /* EOF */