* Sync up to trunk HEAD (r62502).
[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 = Console->LineInsertToggle = FALSE;
573 // LineWakeupMask
574
575 // FIXME: This is terminal-specific !! VV
576 RtlZeroMemory(&Console->Selection, sizeof(CONSOLE_SELECTION_INFO));
577 Console->Selection.dwFlags = CONSOLE_NO_SELECTION;
578 // dwSelectionCursor
579
580 /* Set-up the code page */
581 Console->CodePage = Console->OutputCodePage = ConsoleInfo->CodePage;
582
583 /* Initialize a new text-mode screen buffer with default settings */
584 ScreenBufferInfo.ScreenBufferSize = ConsoleInfo->ScreenBufferSize;
585 ScreenBufferInfo.ScreenAttrib = ConsoleInfo->ScreenAttrib;
586 ScreenBufferInfo.PopupAttrib = ConsoleInfo->PopupAttrib;
587 ScreenBufferInfo.IsCursorVisible = TRUE;
588 ScreenBufferInfo.CursorSize = ConsoleInfo->CursorSize;
589
590 InitializeListHead(&Console->BufferList);
591 Status = ConDrvCreateScreenBuffer(&NewBuffer,
592 Console,
593 CONSOLE_TEXTMODE_BUFFER,
594 &ScreenBufferInfo);
595 if (!NT_SUCCESS(Status))
596 {
597 DPRINT1("ConDrvCreateScreenBuffer: failed, Status = 0x%08lx\n", Status);
598 CloseHandle(Console->InputBuffer.ActiveEvent);
599 DeleteCriticalSection(&Console->Lock);
600 ConsoleFreeHeap(Console);
601 return Status;
602 }
603 /* Make the new screen buffer active */
604 Console->ActiveBuffer = NewBuffer;
605 InitializeListHead(&Console->WriteWaitQueue);
606 Console->PauseFlags = 0;
607 Console->UnpauseEvent = NULL;
608
609 /*
610 * Initialize the alias and history buffers
611 */
612 Console->Aliases = NULL;
613 InitializeListHead(&Console->HistoryBuffers);
614 Console->HistoryBufferSize = ConsoleInfo->HistoryBufferSize;
615 Console->NumberOfHistoryBuffers = ConsoleInfo->NumberOfHistoryBuffers;
616 Console->HistoryNoDup = ConsoleInfo->HistoryNoDup;
617
618 /* Initialize the console title */
619 ConsoleCreateUnicodeString(&Console->OriginalTitle, ConsoleInfo->ConsoleTitle);
620 // if (ConsoleInfo.ConsoleTitle[0] == L'\0')
621 // {
622 // if (LoadStringW(ConSrvDllInstance, IDS_CONSOLE_TITLE, DefaultTitle, sizeof(DefaultTitle) / sizeof(DefaultTitle[0])))
623 // {
624 // ConsoleCreateUnicodeString(&Console->Title, DefaultTitle);
625 // }
626 // else
627 // {
628 // ConsoleCreateUnicodeString(&Console->Title, L"ReactOS Console");
629 // }
630 // }
631 // else
632 // {
633 ConsoleCreateUnicodeString(&Console->Title, ConsoleInfo->ConsoleTitle);
634 // }
635
636 /* Lock the console until its initialization is finished */
637 // EnterCriticalSection(&Console->Lock);
638
639 DPRINT("Console initialized\n");
640
641 /* All went right, so add the console to the list */
642 Status = InsertConsole(&ConsoleHandle, Console);
643 if (!NT_SUCCESS(Status))
644 {
645 /* Fail */
646 ConDrvDeleteConsole(Console);
647 return Status;
648 }
649
650 /* The initialization is finished */
651 DPRINT("Change state\n");
652 Console->State = CONSOLE_RUNNING;
653
654 /* Unlock the console */
655 // LeaveCriticalSection(&Console->Lock);
656
657 /* Return the newly created console to the caller and a success code too */
658 *NewConsoleHandle = ConsoleHandle;
659 *NewConsole = Console;
660 return STATUS_SUCCESS;
661 }
662
663 NTSTATUS NTAPI
664 ConDrvRegisterFrontEnd(IN PCONSOLE Console,
665 IN PFRONTEND FrontEnd)
666 {
667 NTSTATUS Status;
668
669 if (Console == NULL || FrontEnd == NULL)
670 return STATUS_INVALID_PARAMETER;
671
672 /* FIXME: Lock the console before ?? */
673
674 /*
675 * Attach the frontend to the console. Use now the TermIFace of the console,
676 * and not the user-defined temporary FrontEnd pointer.
677 */
678 Console->TermIFace = *FrontEnd;
679 Console->TermIFace.Console = Console;
680
681 /* Initialize the frontend AFTER having attached it to the console */
682 DPRINT("Finish initialization of frontend\n");
683 Status = Console->TermIFace.Vtbl->InitFrontEnd(&Console->TermIFace, Console);
684 if (!NT_SUCCESS(Status))
685 {
686 DPRINT1("FrontEnd initialization failed, Status = 0x%08lx\n", Status);
687
688 /* We failed, detach the frontend from the console */
689 FrontEnd->Console = NULL; // For the caller
690 ResetFrontEnd(Console);
691
692 return Status;
693 }
694
695 /* Copy buffer contents to screen */
696 // FrontEnd.Draw();
697 // ConioDrawConsole(Console);
698 DPRINT("Console drawn\n");
699
700 DPRINT("Terminal FrontEnd initialization done\n");
701 return STATUS_SUCCESS;
702 }
703
704 NTSTATUS NTAPI
705 ConDrvDeregisterFrontEnd(IN PCONSOLE Console)
706 {
707 if (Console == NULL) return STATUS_INVALID_PARAMETER;
708
709 /* FIXME: Lock the console before ?? */
710
711 /* Deinitialize the frontend BEFORE detaching it from the console */
712 Console->TermIFace.Vtbl->DeinitFrontEnd(&Console->TermIFace/*, Console*/);
713
714 /*
715 * Detach the frontend from the console:
716 * reinitialize the frontend interface.
717 */
718 ResetFrontEnd(Console);
719
720 DPRINT("Terminal FrontEnd unregistered\n");
721 return STATUS_SUCCESS;
722 }
723
724 VOID NTAPI
725 ConDrvDeleteConsole(IN PCONSOLE Console)
726 {
727 DPRINT("ConDrvDeleteConsole(0x%p)\n", Console);
728
729 /*
730 * Forbid validation of any console by other threads
731 * during the deletion of this console.
732 */
733 ConDrvLockConsoleListExclusive();
734
735 /*
736 * If the console is already being destroyed, i.e. not running
737 * or finishing to be initialized, just return.
738 */
739 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE) &&
740 !ConDrvValidateConsoleUnsafe(Console, CONSOLE_INITIALIZING, TRUE))
741 {
742 /* Unlock the console list and return */
743 ConDrvUnlockConsoleList();
744 return;
745 }
746
747 /*
748 * We are about to be destroyed. Signal it to other people
749 * so that they can terminate what they are doing, and that
750 * they cannot longer validate the console.
751 */
752 Console->State = CONSOLE_TERMINATING;
753
754 /*
755 * Allow other threads to finish their job: basically, unlock
756 * all other calls to EnterCriticalSection(&Console->Lock); by
757 * ConDrvValidateConsole(Unsafe) functions so that they just see
758 * that we are not in CONSOLE_RUNNING state anymore, or unlock
759 * other concurrent calls to ConDrvDeleteConsole so that they
760 * can see that we are in fact already deleting the console.
761 */
762 LeaveCriticalSection(&Console->Lock);
763 ConDrvUnlockConsoleList();
764
765 /* FIXME: Send a terminate message to all the processes owning this console */
766
767 /* Cleanup the UI-oriented part */
768 DPRINT("Deregister console\n");
769 ConDrvDeregisterFrontEnd(Console);
770 DPRINT("Console deregistered\n");
771
772 /***
773 * Check that the console is in terminating state before continuing
774 * (the cleanup code must not change the state of the console...
775 * ...unless to cancel console deletion ?).
776 ***/
777
778 ConDrvLockConsoleListExclusive();
779
780 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_TERMINATING, TRUE))
781 {
782 ConDrvUnlockConsoleList();
783 return;
784 }
785
786 /* We are now in destruction */
787 Console->State = CONSOLE_IN_DESTRUCTION;
788
789 /* We really delete the console. Reset the count to be sure. */
790 Console->ReferenceCount = 0;
791
792 /* Remove the console from the list */
793 RemoveConsoleByPointer(Console);
794
795 /* Discard all entries in the input event queue */
796 PurgeInputBuffer(Console);
797
798 if (Console->LineBuffer) ConsoleFreeHeap(Console->LineBuffer);
799
800 IntDeleteAllAliases(Console);
801 HistoryDeleteBuffers(Console);
802
803 ConioDeleteScreenBuffer(Console->ActiveBuffer);
804 Console->ActiveBuffer = NULL;
805 if (!IsListEmpty(&Console->BufferList))
806 {
807 DPRINT1("BUG: screen buffer list not empty\n");
808 ASSERT(FALSE);
809 }
810
811 /**/ CloseHandle(Console->InputBuffer.ActiveEvent); /**/
812 if (Console->UnpauseEvent) CloseHandle(Console->UnpauseEvent);
813
814 ConsoleFreeUnicodeString(&Console->OriginalTitle);
815 ConsoleFreeUnicodeString(&Console->Title);
816
817 DPRINT("ConDrvDeleteConsole - Unlocking\n");
818 LeaveCriticalSection(&Console->Lock);
819 DPRINT("ConDrvDeleteConsole - Destroying lock\n");
820 DeleteCriticalSection(&Console->Lock);
821 DPRINT("ConDrvDeleteConsole - Lock destroyed ; freeing console\n");
822
823 ConsoleFreeHeap(Console);
824 DPRINT("ConDrvDeleteConsole - Console destroyed\n");
825
826 /* Unlock the console list and return */
827 ConDrvUnlockConsoleList();
828 }
829
830
831 /* PUBLIC DRIVER APIS *********************************************************/
832
833 NTSTATUS NTAPI
834 ConDrvGetConsoleMode(IN PCONSOLE Console,
835 IN PCONSOLE_IO_OBJECT Object,
836 OUT PULONG ConsoleMode)
837 {
838 NTSTATUS Status = STATUS_SUCCESS;
839
840 if (Console == NULL || Object == NULL || ConsoleMode == NULL)
841 return STATUS_INVALID_PARAMETER;
842
843 /* Validity check */
844 ASSERT(Console == Object->Console);
845
846 /*** FIXME: */ *ConsoleMode = 0; /***/
847
848 if (INPUT_BUFFER == Object->Type)
849 {
850 PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
851
852 *ConsoleMode = InputBuffer->Mode;
853
854 if (Console->QuickEdit || Console->InsertMode)
855 {
856 // Windows does this, even if it's not documented on MSDN
857 *ConsoleMode |= ENABLE_EXTENDED_FLAGS;
858
859 if (Console->QuickEdit ) *ConsoleMode |= ENABLE_QUICK_EDIT_MODE;
860 if (Console->InsertMode) *ConsoleMode |= ENABLE_INSERT_MODE;
861 }
862 }
863 else if (TEXTMODE_BUFFER == Object->Type || GRAPHICS_BUFFER == Object->Type)
864 {
865 PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
866 *ConsoleMode = Buffer->Mode;
867 }
868 else
869 {
870 Status = STATUS_INVALID_HANDLE;
871 }
872
873 return Status;
874 }
875
876 NTSTATUS NTAPI
877 ConDrvSetConsoleMode(IN PCONSOLE Console,
878 IN PCONSOLE_IO_OBJECT Object,
879 IN ULONG ConsoleMode)
880 {
881 #define CONSOLE_VALID_CONTROL_MODES ( ENABLE_EXTENDED_FLAGS | ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE )
882 #define CONSOLE_VALID_INPUT_MODES ( ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | \
883 ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT | \
884 ENABLE_MOUSE_INPUT )
885 #define CONSOLE_VALID_OUTPUT_MODES ( ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT )
886
887 NTSTATUS Status = STATUS_SUCCESS;
888
889 if (Console == NULL || Object == NULL)
890 return STATUS_INVALID_PARAMETER;
891
892 /* Validity check */
893 ASSERT(Console == Object->Console);
894
895 if (INPUT_BUFFER == Object->Type)
896 {
897 PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
898
899 DPRINT("SetConsoleMode(Input, %d)\n", ConsoleMode);
900
901 /*
902 * 1. Only the presence of valid mode flags is allowed.
903 */
904 if (ConsoleMode & ~(CONSOLE_VALID_INPUT_MODES | CONSOLE_VALID_CONTROL_MODES))
905 {
906 Status = STATUS_INVALID_PARAMETER;
907 goto Quit;
908 }
909
910 /*
911 * 2. If we use control mode flags without ENABLE_EXTENDED_FLAGS,
912 * then consider the flags invalid.
913 *
914 if ( (ConsoleMode & CONSOLE_VALID_CONTROL_MODES) &&
915 (ConsoleMode & ENABLE_EXTENDED_FLAGS) == 0 )
916 {
917 Status = STATUS_INVALID_PARAMETER;
918 goto Quit;
919 }
920 */
921
922 /*
923 * 3. Now we can continue.
924 */
925 if (ConsoleMode & CONSOLE_VALID_CONTROL_MODES)
926 {
927 Console->QuickEdit = !!(ConsoleMode & ENABLE_QUICK_EDIT_MODE);
928 Console->InsertMode = !!(ConsoleMode & ENABLE_INSERT_MODE);
929 }
930 InputBuffer->Mode = (ConsoleMode & CONSOLE_VALID_INPUT_MODES);
931 }
932 else if (TEXTMODE_BUFFER == Object->Type || GRAPHICS_BUFFER == Object->Type)
933 {
934 PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
935
936 DPRINT("SetConsoleMode(Output, %d)\n", ConsoleMode);
937
938 if (ConsoleMode & ~CONSOLE_VALID_OUTPUT_MODES)
939 {
940 Status = STATUS_INVALID_PARAMETER;
941 }
942 else
943 {
944 Buffer->Mode = (ConsoleMode & CONSOLE_VALID_OUTPUT_MODES);
945 }
946 }
947 else
948 {
949 Status = STATUS_INVALID_HANDLE;
950 }
951
952 Quit:
953 return Status;
954 }
955
956 NTSTATUS NTAPI
957 ConDrvGetConsoleTitle(IN PCONSOLE Console,
958 IN OUT PWCHAR Title,
959 IN OUT PULONG BufLength)
960 {
961 ULONG Length;
962
963 if (Console == NULL || Title == NULL || BufLength == NULL)
964 return STATUS_INVALID_PARAMETER;
965
966 /* Copy title of the console to the user title buffer */
967 if (*BufLength >= sizeof(WCHAR))
968 {
969 Length = min(*BufLength - sizeof(WCHAR), Console->Title.Length);
970 RtlCopyMemory(Title, Console->Title.Buffer, Length);
971 Title[Length / sizeof(WCHAR)] = L'\0';
972 }
973
974 *BufLength = Console->Title.Length;
975
976 return STATUS_SUCCESS;
977 }
978
979 NTSTATUS NTAPI
980 ConDrvSetConsoleTitle(IN PCONSOLE Console,
981 IN PWCHAR Title,
982 IN ULONG BufLength)
983 {
984 PWCHAR Buffer;
985
986 if (Console == NULL || Title == NULL)
987 return STATUS_INVALID_PARAMETER;
988
989 /* Allocate a new buffer to hold the new title (NULL-terminated) */
990 Buffer = ConsoleAllocHeap(0, BufLength + sizeof(WCHAR));
991 if (!Buffer) return STATUS_NO_MEMORY;
992
993 /* Free the old title */
994 ConsoleFreeUnicodeString(&Console->Title);
995
996 /* Copy title to console */
997 Console->Title.Buffer = Buffer;
998 Console->Title.Length = BufLength;
999 Console->Title.MaximumLength = Console->Title.Length + sizeof(WCHAR);
1000 RtlCopyMemory(Console->Title.Buffer, Title, Console->Title.Length);
1001 Console->Title.Buffer[Console->Title.Length / sizeof(WCHAR)] = L'\0';
1002
1003 // TermChangeTitle(Console);
1004 return STATUS_SUCCESS;
1005 }
1006
1007 NTSTATUS NTAPI
1008 ConDrvGetConsoleCP(IN PCONSOLE Console,
1009 OUT PUINT CodePage,
1010 IN BOOLEAN OutputCP)
1011 {
1012 if (Console == NULL || CodePage == NULL)
1013 return STATUS_INVALID_PARAMETER;
1014
1015 *CodePage = (OutputCP ? Console->OutputCodePage : Console->CodePage);
1016
1017 return STATUS_SUCCESS;
1018 }
1019
1020 NTSTATUS NTAPI
1021 ConDrvSetConsoleCP(IN PCONSOLE Console,
1022 IN UINT CodePage,
1023 IN BOOLEAN OutputCP)
1024 {
1025 if (Console == NULL || !IsValidCodePage(CodePage))
1026 return STATUS_INVALID_PARAMETER;
1027
1028 if (OutputCP)
1029 Console->OutputCodePage = CodePage;
1030 else
1031 Console->CodePage = CodePage;
1032
1033 return STATUS_SUCCESS;
1034 }
1035
1036 NTSTATUS NTAPI
1037 ConDrvGetConsoleProcessList(IN PCONSOLE Console,
1038 IN OUT PULONG ProcessIdsList,
1039 IN ULONG MaxIdListItems,
1040 OUT PULONG ProcessIdsTotal)
1041 {
1042 PCONSOLE_PROCESS_DATA current;
1043 PLIST_ENTRY current_entry;
1044
1045 if (Console == NULL || ProcessIdsList == NULL || ProcessIdsTotal == NULL)
1046 return STATUS_INVALID_PARAMETER;
1047
1048 *ProcessIdsTotal = 0;
1049
1050 for (current_entry = Console->ProcessList.Flink;
1051 current_entry != &Console->ProcessList;
1052 current_entry = current_entry->Flink)
1053 {
1054 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
1055 if (++(*ProcessIdsTotal) <= MaxIdListItems)
1056 {
1057 *ProcessIdsList++ = HandleToUlong(current->Process->ClientId.UniqueProcess);
1058 }
1059 }
1060
1061 return STATUS_SUCCESS;
1062 }
1063
1064 // ConDrvGenerateConsoleCtrlEvent
1065 NTSTATUS NTAPI
1066 ConDrvConsoleProcessCtrlEvent(IN PCONSOLE Console,
1067 IN ULONG ProcessGroupId,
1068 IN ULONG Event)
1069 {
1070 NTSTATUS Status = STATUS_SUCCESS;
1071 PLIST_ENTRY current_entry;
1072 PCONSOLE_PROCESS_DATA current;
1073
1074 /* If the console is already being destroyed, just return */
1075 if (!ConDrvValidateConsoleState(Console, CONSOLE_RUNNING))
1076 return STATUS_UNSUCCESSFUL;
1077
1078 /*
1079 * Loop through the process list, from the most recent process
1080 * (the active one) to the oldest one (the first created, i.e.
1081 * the console leader process), and for each, send an event
1082 * (new processes are inserted at the head of the console process list).
1083 */
1084 current_entry = Console->ProcessList.Flink;
1085 while (current_entry != &Console->ProcessList)
1086 {
1087 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
1088 current_entry = current_entry->Flink;
1089
1090 /*
1091 * Only processes belonging to the same process group are signaled.
1092 * If the process group ID is zero, then all the processes are signaled.
1093 */
1094 if (ProcessGroupId == 0 || current->Process->ProcessGroupId == ProcessGroupId)
1095 {
1096 Status = ConDrvConsoleCtrlEvent(Event, current);
1097 }
1098 }
1099
1100 return Status;
1101 }
1102
1103 /* EOF */