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