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