[KERNEL32]
[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 <coninput.h>
16
17 #define NDEBUG
18 #include <debug.h>
19
20
21 /* GLOBALS ********************************************************************/
22
23 static ULONG CurrentConsoleID = 0;
24
25 /* Linked list of consoles */
26 static LIST_ENTRY ConsoleList;
27 static RTL_RESOURCE ListLock;
28
29 #define ConDrvLockConsoleListExclusive() \
30 RtlAcquireResourceExclusive(&ListLock, TRUE)
31
32 #define ConDrvLockConsoleListShared() \
33 RtlAcquireResourceShared(&ListLock, TRUE)
34
35 #define ConDrvUnlockConsoleList() \
36 RtlReleaseResource(&ListLock)
37
38
39 static NTSTATUS
40 InsertConsole(IN PCONSOLE Console)
41 {
42 ASSERT(Console);
43
44 /* All went right, so add the console to the list */
45 ConDrvLockConsoleListExclusive();
46
47 DPRINT1("Insert in the list\n");
48 InsertTailList(&ConsoleList, &Console->ListEntry);
49
50 // FIXME: Move this code to the caller function!!
51 /* Get a new console ID */
52 _InterlockedExchange((PLONG)&Console->ConsoleID, CurrentConsoleID);
53 _InterlockedIncrement((PLONG)&CurrentConsoleID);
54
55 /* Unlock the console list and return success */
56 ConDrvUnlockConsoleList();
57 return STATUS_SUCCESS;
58 }
59
60 static NTSTATUS
61 RemoveConsole(IN PCONSOLE Console)
62 {
63 // ASSERT(Console);
64 if (!Console) return STATUS_INVALID_PARAMETER;
65
66 /* Remove the console from the list */
67 ConDrvLockConsoleListExclusive();
68
69 RemoveEntryList(&Console->ListEntry);
70
71 /* Unlock the console list and return success */
72 ConDrvUnlockConsoleList();
73 return STATUS_SUCCESS;
74 }
75
76
77 /* PRIVATE FUNCTIONS **********************************************************/
78
79 // Adapted from reactos/lib/rtl/unicode.c, RtlCreateUnicodeString line 2180
80 static BOOLEAN
81 ConsoleCreateUnicodeString(IN OUT PUNICODE_STRING UniDest,
82 IN PCWSTR Source)
83 {
84 SIZE_T Size = (wcslen(Source) + 1) * sizeof(WCHAR);
85 if (Size > MAXUSHORT) return FALSE;
86
87 UniDest->Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Size);
88 if (UniDest->Buffer == NULL) return FALSE;
89
90 RtlCopyMemory(UniDest->Buffer, Source, Size);
91 UniDest->MaximumLength = (USHORT)Size;
92 UniDest->Length = (USHORT)Size - sizeof(WCHAR);
93
94 return TRUE;
95 }
96
97 // Adapted from reactos/lib/rtl/unicode.c, RtlFreeUnicodeString line 431
98 static VOID
99 ConsoleFreeUnicodeString(IN PUNICODE_STRING UnicodeString)
100 {
101 if (UnicodeString->Buffer)
102 {
103 ConsoleFreeHeap(UnicodeString->Buffer);
104 RtlZeroMemory(UnicodeString, sizeof(UNICODE_STRING));
105 }
106 }
107
108 VOID NTAPI
109 ConDrvPause(PCONSOLE Console)
110 {
111 if (!Console->UnpauseEvent)
112 Console->UnpauseEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
113 }
114
115 VOID NTAPI
116 ConDrvUnpause(PCONSOLE Console)
117 {
118 if (Console->UnpauseEvent)
119 {
120 SetEvent(Console->UnpauseEvent);
121 CloseHandle(Console->UnpauseEvent);
122 Console->UnpauseEvent = NULL;
123 }
124 }
125
126
127 /*
128 * Console accessibility check helpers
129 */
130
131 BOOLEAN NTAPI
132 ConDrvValidateConsoleState(IN PCONSOLE Console,
133 IN CONSOLE_STATE ExpectedState)
134 {
135 // if (!Console) return FALSE;
136
137 /* The console must be locked */
138 // ASSERT(Console_locked);
139
140 return (Console->State == ExpectedState);
141 }
142
143 BOOLEAN NTAPI
144 ConDrvValidateConsoleUnsafe(IN PCONSOLE Console,
145 IN CONSOLE_STATE ExpectedState,
146 IN BOOLEAN LockConsole)
147 {
148 if (!Console) return FALSE;
149
150 /*
151 * Lock the console to forbid possible console's state changes
152 * (which must be done when the console is already locked).
153 * If we don't want to lock it, it's because the lock is already
154 * held. So there must be no problems.
155 */
156 if (LockConsole) EnterCriticalSection(&Console->Lock);
157
158 // ASSERT(Console_locked);
159
160 /* Check whether the console's state is what we expect */
161 if (!ConDrvValidateConsoleState(Console, ExpectedState))
162 {
163 if (LockConsole) LeaveCriticalSection(&Console->Lock);
164 return FALSE;
165 }
166
167 return TRUE;
168 }
169
170
171 /* CONSOLE INITIALIZATION FUNCTIONS *******************************************/
172
173 VOID NTAPI
174 ConDrvInitConsoleSupport(VOID)
175 {
176 DPRINT("CONSRV: ConDrvInitConsoleSupport()\n");
177
178 /* Initialize the console list and its lock */
179 InitializeListHead(&ConsoleList);
180 RtlInitializeResource(&ListLock);
181
182 /* Should call LoadKeyboardLayout */
183 }
184
185 /* For resetting the terminal - defined in dummyterm.c */
186 VOID ResetTerminal(IN PCONSOLE Console);
187
188 NTSTATUS NTAPI
189 ConDrvInitConsole(OUT PCONSOLE* NewConsole,
190 IN PCONSOLE_INFO ConsoleInfo)
191 {
192 NTSTATUS Status;
193 SECURITY_ATTRIBUTES SecurityAttributes;
194 // CONSOLE_INFO CapturedConsoleInfo;
195 TEXTMODE_BUFFER_INFO ScreenBufferInfo;
196 PCONSOLE Console;
197 PCONSOLE_SCREEN_BUFFER NewBuffer;
198 #if 0
199 WCHAR DefaultTitle[128];
200 #endif
201
202 if (NewConsole == NULL || ConsoleInfo == NULL)
203 return STATUS_INVALID_PARAMETER;
204
205 *NewConsole = NULL;
206
207 /*
208 * Allocate a new console
209 */
210 Console = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(*Console));
211 if (NULL == Console)
212 {
213 DPRINT1("Not enough memory for console creation.\n");
214 return STATUS_NO_MEMORY;
215 }
216
217 /*
218 * Fix the screen buffer size if needed. The rule is:
219 * ScreenBufferSize >= ConsoleSize
220 */
221 if (ConsoleInfo->ScreenBufferSize.X < ConsoleInfo->ConsoleSize.X)
222 ConsoleInfo->ScreenBufferSize.X = ConsoleInfo->ConsoleSize.X;
223 if (ConsoleInfo->ScreenBufferSize.Y < ConsoleInfo->ConsoleSize.Y)
224 ConsoleInfo->ScreenBufferSize.Y = ConsoleInfo->ConsoleSize.Y;
225
226 /*
227 * Initialize the console
228 */
229 Console->State = CONSOLE_INITIALIZING;
230 Console->ReferenceCount = 0;
231 InitializeCriticalSection(&Console->Lock);
232
233 /* Initialize the terminal interface */
234 ResetTerminal(Console);
235
236 memcpy(Console->Colors, ConsoleInfo->Colors, sizeof(ConsoleInfo->Colors));
237 Console->ConsoleSize = ConsoleInfo->ConsoleSize;
238 Console->FixedSize = FALSE; // Value by default; is reseted by the terminals if needed.
239
240 /*
241 * Initialize the input buffer
242 */
243 ConSrvInitObject(&Console->InputBuffer.Header, INPUT_BUFFER, Console);
244
245 SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
246 SecurityAttributes.lpSecurityDescriptor = NULL;
247 SecurityAttributes.bInheritHandle = TRUE;
248 Console->InputBuffer.ActiveEvent = CreateEventW(&SecurityAttributes, TRUE, FALSE, NULL);
249 if (NULL == Console->InputBuffer.ActiveEvent)
250 {
251 DeleteCriticalSection(&Console->Lock);
252 ConsoleFreeHeap(Console);
253 return STATUS_UNSUCCESSFUL;
254 }
255
256 Console->InputBuffer.InputBufferSize = 0; // FIXME!
257 InitializeListHead(&Console->InputBuffer.InputEvents);
258 Console->InputBuffer.Mode = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
259 ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT;
260
261 Console->InsertMode = ConsoleInfo->InsertMode;
262 Console->LineBuffer = NULL;
263 Console->LineMaxSize = Console->LineSize = Console->LinePos = 0;
264 Console->LineComplete = Console->LineUpPressed = FALSE;
265 Console->LineInsertToggle = Console->InsertMode;
266 // LineWakeupMask
267
268 /* Set-up the code page */
269 Console->InputCodePage = Console->OutputCodePage = ConsoleInfo->CodePage;
270
271 /* Initialize a new text-mode screen buffer with default settings */
272 ScreenBufferInfo.ScreenBufferSize = ConsoleInfo->ScreenBufferSize;
273 ScreenBufferInfo.ScreenAttrib = ConsoleInfo->ScreenAttrib;
274 ScreenBufferInfo.PopupAttrib = ConsoleInfo->PopupAttrib;
275 ScreenBufferInfo.IsCursorVisible = TRUE;
276 ScreenBufferInfo.CursorSize = ConsoleInfo->CursorSize;
277
278 InitializeListHead(&Console->BufferList);
279 Status = ConDrvCreateScreenBuffer(&NewBuffer,
280 Console,
281 CONSOLE_TEXTMODE_BUFFER,
282 &ScreenBufferInfo);
283 if (!NT_SUCCESS(Status))
284 {
285 DPRINT1("ConDrvCreateScreenBuffer: failed, Status = 0x%08lx\n", Status);
286 CloseHandle(Console->InputBuffer.ActiveEvent);
287 DeleteCriticalSection(&Console->Lock);
288 ConsoleFreeHeap(Console);
289 return Status;
290 }
291 /* Make the new screen buffer active */
292 Console->ActiveBuffer = NewBuffer;
293 Console->UnpauseEvent = NULL;
294
295 /* Initialize the console title */
296 ConsoleCreateUnicodeString(&Console->OriginalTitle, ConsoleInfo->ConsoleTitle);
297 #if 0
298 if (ConsoleInfo.ConsoleTitle[0] == L'\0')
299 {
300 if (LoadStringW(ConSrvDllInstance, IDS_CONSOLE_TITLE, DefaultTitle, sizeof(DefaultTitle) / sizeof(DefaultTitle[0])))
301 {
302 ConsoleCreateUnicodeString(&Console->Title, DefaultTitle);
303 }
304 else
305 {
306 ConsoleCreateUnicodeString(&Console->Title, L"ReactOS Console");
307 }
308 }
309 else
310 {
311 #endif
312 ConsoleCreateUnicodeString(&Console->Title, ConsoleInfo->ConsoleTitle);
313 #if 0
314 }
315 #endif
316
317 DPRINT("Console initialized\n");
318
319 /* All went right, so add the console to the list */
320 Status = InsertConsole(Console);
321 if (!NT_SUCCESS(Status))
322 {
323 /* Fail */
324 ConDrvDeleteConsole(Console);
325 return Status;
326 }
327
328 /* The initialization is finished */
329 DPRINT("Change state\n");
330 Console->State = CONSOLE_RUNNING;
331
332 /* Return the newly created console to the caller and a success code too */
333 *NewConsole = Console;
334 return STATUS_SUCCESS;
335 }
336
337 NTSTATUS NTAPI
338 ConDrvRegisterTerminal(IN PCONSOLE Console,
339 IN PTERMINAL Terminal)
340 {
341 NTSTATUS Status;
342
343 if (Console == NULL || Terminal == NULL)
344 return STATUS_INVALID_PARAMETER;
345
346 /* FIXME: Lock the console before ?? */
347
348 /*
349 * Attach the terminal to the console. Use now the TermIFace of the console,
350 * and not the user-defined temporary Terminal pointer.
351 */
352 Console->TermIFace = *Terminal;
353 Console->TermIFace.Console = Console;
354
355 /* Initialize the terminal AFTER having attached it to the console */
356 DPRINT("Finish initialization of terminal\n");
357 Status = Console->TermIFace.Vtbl->InitTerminal(&Console->TermIFace, Console);
358 if (!NT_SUCCESS(Status))
359 {
360 DPRINT1("Terminal initialization failed, Status = 0x%08lx\n", Status);
361
362 /* We failed, detach the terminal from the console */
363 Terminal->Console = NULL; // For the caller
364 ResetTerminal(Console);
365
366 return Status;
367 }
368
369 /* Copy buffer contents to screen */
370 // Terminal.Draw();
371 // ConioDrawConsole(Console);
372 DPRINT("Console drawn\n");
373
374 DPRINT("Terminal initialization done\n");
375 return STATUS_SUCCESS;
376 }
377
378 NTSTATUS NTAPI
379 ConDrvDeregisterTerminal(IN PCONSOLE Console)
380 {
381 if (Console == NULL) return STATUS_INVALID_PARAMETER;
382
383 /* FIXME: Lock the console before ?? */
384
385 /* Deinitialize the terminal BEFORE detaching it from the console */
386 Console->TermIFace.Vtbl->DeinitTerminal(&Console->TermIFace/*, Console*/);
387
388 /*
389 * Detach the terminal from the console:
390 * reinitialize the terminal interface.
391 */
392 ResetTerminal(Console);
393
394 DPRINT("Terminal unregistered\n");
395 return STATUS_SUCCESS;
396 }
397
398 VOID NTAPI
399 ConDrvDeleteConsole(IN PCONSOLE Console)
400 {
401 DPRINT("ConDrvDeleteConsole(0x%p)\n", Console);
402
403 /*
404 * Forbid validation of any console by other threads
405 * during the deletion of this console.
406 */
407 ConDrvLockConsoleListExclusive();
408
409 /*
410 * If the console is already being destroyed, i.e. not running
411 * or finishing to be initialized, just return.
412 */
413 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE) &&
414 !ConDrvValidateConsoleUnsafe(Console, CONSOLE_INITIALIZING, TRUE))
415 {
416 /* Unlock the console list and return */
417 ConDrvUnlockConsoleList();
418 return;
419 }
420
421 /*
422 * We are about to be destroyed. Signal it to other people
423 * so that they can terminate what they are doing, and that
424 * they cannot longer validate the console.
425 */
426 Console->State = CONSOLE_TERMINATING;
427
428 /*
429 * Allow other threads to finish their job: basically, unlock
430 * all other calls to EnterCriticalSection(&Console->Lock); by
431 * ConDrvValidateConsoleUnsafe functions so that they just see
432 * that we are not in CONSOLE_RUNNING state anymore, or unlock
433 * other concurrent calls to ConDrvDeleteConsole so that they
434 * can see that we are in fact already deleting the console.
435 */
436 LeaveCriticalSection(&Console->Lock);
437 ConDrvUnlockConsoleList();
438
439 /* FIXME: Send a terminate message to all the processes owning this console */
440
441 /* Cleanup the UI-oriented part */
442 DPRINT("Deregister console\n");
443 ConDrvDeregisterTerminal(Console);
444 DPRINT("Console deregistered\n");
445
446 /***
447 * Check that the console is in terminating state before continuing
448 * (the cleanup code must not change the state of the console...
449 * ...unless to cancel console deletion ?).
450 ***/
451
452 ConDrvLockConsoleListExclusive();
453
454 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_TERMINATING, TRUE))
455 {
456 ConDrvUnlockConsoleList();
457 return;
458 }
459
460 /* We are now in destruction */
461 Console->State = CONSOLE_IN_DESTRUCTION;
462
463 /* We really delete the console. Reset the count to be sure. */
464 Console->ReferenceCount = 0;
465
466 /* Remove the console from the list */
467 RemoveConsole(Console);
468
469 /* Discard all entries in the input event queue */
470 PurgeInputBuffer(Console);
471 if (Console->LineBuffer) ConsoleFreeHeap(Console->LineBuffer);
472
473 /* Delete the last screen buffer */
474 ConioDeleteScreenBuffer(Console->ActiveBuffer);
475 Console->ActiveBuffer = NULL;
476 if (!IsListEmpty(&Console->BufferList))
477 {
478 DPRINT1("BUG: screen buffer list not empty\n");
479 ASSERT(FALSE);
480 }
481
482 /**/ CloseHandle(Console->InputBuffer.ActiveEvent); /**/
483 if (Console->UnpauseEvent) CloseHandle(Console->UnpauseEvent);
484
485 ConsoleFreeUnicodeString(&Console->OriginalTitle);
486 ConsoleFreeUnicodeString(&Console->Title);
487
488 DPRINT("ConDrvDeleteConsole - Unlocking\n");
489 LeaveCriticalSection(&Console->Lock);
490 DPRINT("ConDrvDeleteConsole - Destroying lock\n");
491 DeleteCriticalSection(&Console->Lock);
492 DPRINT("ConDrvDeleteConsole - Lock destroyed ; freeing console\n");
493
494 ConsoleFreeHeap(Console);
495 DPRINT("ConDrvDeleteConsole - Console destroyed\n");
496
497 /* Unlock the console list and return */
498 ConDrvUnlockConsoleList();
499 }
500
501
502 /* PUBLIC DRIVER APIS *********************************************************/
503
504 NTSTATUS NTAPI
505 ConDrvGetConsoleMode(IN PCONSOLE Console,
506 IN PCONSOLE_IO_OBJECT Object,
507 OUT PULONG ConsoleMode)
508 {
509 NTSTATUS Status = STATUS_SUCCESS;
510
511 if (Console == NULL || Object == NULL || ConsoleMode == NULL)
512 return STATUS_INVALID_PARAMETER;
513
514 /* Validity check */
515 ASSERT(Console == Object->Console);
516
517 /*** FIXME: */ *ConsoleMode = 0; /***/
518
519 if (INPUT_BUFFER == Object->Type)
520 {
521 PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
522
523 *ConsoleMode = InputBuffer->Mode;
524
525 if (Console->QuickEdit || Console->InsertMode)
526 {
527 // Windows does this, even if it's not documented on MSDN
528 *ConsoleMode |= ENABLE_EXTENDED_FLAGS;
529
530 if (Console->QuickEdit ) *ConsoleMode |= ENABLE_QUICK_EDIT_MODE;
531 if (Console->InsertMode) *ConsoleMode |= ENABLE_INSERT_MODE;
532 }
533 }
534 else if (TEXTMODE_BUFFER == Object->Type || GRAPHICS_BUFFER == Object->Type)
535 {
536 PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
537 *ConsoleMode = Buffer->Mode;
538 }
539 else
540 {
541 Status = STATUS_INVALID_HANDLE;
542 }
543
544 return Status;
545 }
546
547 NTSTATUS NTAPI
548 ConDrvSetConsoleMode(IN PCONSOLE Console,
549 IN PCONSOLE_IO_OBJECT Object,
550 IN ULONG ConsoleMode)
551 {
552 #define CONSOLE_VALID_CONTROL_MODES ( ENABLE_EXTENDED_FLAGS | \
553 ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE )
554 #define CONSOLE_VALID_INPUT_MODES ( ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | \
555 ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT | \
556 ENABLE_MOUSE_INPUT )
557 #define CONSOLE_VALID_OUTPUT_MODES ( ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT )
558
559 NTSTATUS Status = STATUS_SUCCESS;
560
561 if (Console == NULL || Object == NULL)
562 return STATUS_INVALID_PARAMETER;
563
564 /* Validity check */
565 ASSERT(Console == Object->Console);
566
567 if (INPUT_BUFFER == Object->Type)
568 {
569 PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
570
571 DPRINT("SetConsoleMode(Input, %d)\n", ConsoleMode);
572
573 /*
574 * 1. Only the presence of valid mode flags is allowed.
575 */
576 if (ConsoleMode & ~(CONSOLE_VALID_INPUT_MODES | CONSOLE_VALID_CONTROL_MODES))
577 {
578 Status = STATUS_INVALID_PARAMETER;
579 goto Quit;
580 }
581
582 /*
583 * 2. If we use control mode flags without ENABLE_EXTENDED_FLAGS,
584 * then consider the flags invalid.
585 *
586 if ( (ConsoleMode & CONSOLE_VALID_CONTROL_MODES) &&
587 (ConsoleMode & ENABLE_EXTENDED_FLAGS) == 0 )
588 {
589 Status = STATUS_INVALID_PARAMETER;
590 goto Quit;
591 }
592 */
593
594 /*
595 * 3. Now we can continue.
596 */
597 if (ConsoleMode & CONSOLE_VALID_CONTROL_MODES)
598 {
599 Console->QuickEdit = !!(ConsoleMode & ENABLE_QUICK_EDIT_MODE);
600 Console->InsertMode = !!(ConsoleMode & ENABLE_INSERT_MODE);
601 }
602 InputBuffer->Mode = (ConsoleMode & CONSOLE_VALID_INPUT_MODES);
603 }
604 else if (TEXTMODE_BUFFER == Object->Type || GRAPHICS_BUFFER == Object->Type)
605 {
606 PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
607
608 DPRINT("SetConsoleMode(Output, %d)\n", ConsoleMode);
609
610 if (ConsoleMode & ~CONSOLE_VALID_OUTPUT_MODES)
611 {
612 Status = STATUS_INVALID_PARAMETER;
613 }
614 else
615 {
616 Buffer->Mode = (ConsoleMode & CONSOLE_VALID_OUTPUT_MODES);
617 }
618 }
619 else
620 {
621 Status = STATUS_INVALID_HANDLE;
622 }
623
624 Quit:
625 return Status;
626 }
627
628 NTSTATUS NTAPI
629 ConDrvGetConsoleTitle(IN PCONSOLE Console,
630 IN BOOLEAN Unicode,
631 IN OUT PVOID TitleBuffer,
632 IN OUT PULONG BufLength)
633 {
634 ULONG Length;
635
636 if (Console == NULL || TitleBuffer == NULL || BufLength == NULL)
637 return STATUS_INVALID_PARAMETER;
638
639 /* Copy title of the console to the user title buffer */
640 if (Unicode)
641 {
642 if (*BufLength >= sizeof(WCHAR))
643 {
644 Length = min(*BufLength - sizeof(WCHAR), Console->Title.Length);
645 RtlCopyMemory(TitleBuffer, Console->Title.Buffer, Length);
646 ((PWCHAR)TitleBuffer)[Length / sizeof(WCHAR)] = L'\0';
647 *BufLength = Length;
648 }
649 else
650 {
651 *BufLength = Console->Title.Length;
652 }
653 }
654 else
655 {
656 if (*BufLength >= sizeof(CHAR))
657 {
658 Length = min(*BufLength - sizeof(CHAR), Console->Title.Length / sizeof(WCHAR));
659 Length = WideCharToMultiByte(Console->InputCodePage, 0,
660 Console->Title.Buffer, Length,
661 TitleBuffer, Length,
662 NULL, NULL);
663 ((PCHAR)TitleBuffer)[Length] = '\0';
664 *BufLength = Length;
665 }
666 else
667 {
668 *BufLength = Console->Title.Length / sizeof(WCHAR);
669 }
670 }
671
672 return STATUS_SUCCESS;
673 }
674
675 NTSTATUS NTAPI
676 ConDrvSetConsoleTitle(IN PCONSOLE Console,
677 IN BOOLEAN Unicode,
678 IN PVOID TitleBuffer,
679 IN ULONG BufLength)
680 {
681 PWCHAR Buffer;
682 ULONG Length;
683
684 if (Console == NULL || TitleBuffer == NULL)
685 return STATUS_INVALID_PARAMETER;
686
687 if (Unicode)
688 {
689 /* Length is in bytes */
690 Length = BufLength;
691 }
692 else
693 {
694 /* Use the console input CP for the conversion */
695 Length = MultiByteToWideChar(Console->InputCodePage, 0,
696 TitleBuffer, BufLength,
697 NULL, 0);
698 /* The returned Length was in number of wchars, convert it in bytes */
699 Length *= sizeof(WCHAR);
700 }
701
702 /* Allocate a new buffer to hold the new title (NULL-terminated) */
703 Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Length + sizeof(WCHAR));
704 if (!Buffer) return STATUS_NO_MEMORY;
705
706 /* Free the old title */
707 ConsoleFreeUnicodeString(&Console->Title);
708
709 /* Copy title to console */
710 Console->Title.Buffer = Buffer;
711 Console->Title.Length = Length;
712 Console->Title.MaximumLength = Console->Title.Length + sizeof(WCHAR);
713
714 if (Unicode)
715 {
716 RtlCopyMemory(Console->Title.Buffer, TitleBuffer, Console->Title.Length);
717 }
718 else
719 {
720 MultiByteToWideChar(Console->InputCodePage, 0,
721 TitleBuffer, BufLength,
722 Console->Title.Buffer,
723 Console->Title.Length / sizeof(WCHAR));
724 }
725
726 /* NULL-terminate */
727 Console->Title.Buffer[Console->Title.Length / sizeof(WCHAR)] = L'\0';
728
729 // TermChangeTitle(Console);
730 return STATUS_SUCCESS;
731 }
732
733 NTSTATUS NTAPI
734 ConDrvGetConsoleCP(IN PCONSOLE Console,
735 OUT PUINT CodePage,
736 IN BOOLEAN OutputCP)
737 {
738 if (Console == NULL || CodePage == NULL)
739 return STATUS_INVALID_PARAMETER;
740
741 *CodePage = (OutputCP ? Console->OutputCodePage : Console->InputCodePage);
742
743 return STATUS_SUCCESS;
744 }
745
746 NTSTATUS NTAPI
747 ConDrvSetConsoleCP(IN PCONSOLE Console,
748 IN UINT CodePage,
749 IN BOOLEAN OutputCP)
750 {
751 if (Console == NULL || !IsValidCodePage(CodePage))
752 return STATUS_INVALID_PARAMETER;
753
754 if (OutputCP)
755 Console->OutputCodePage = CodePage;
756 else
757 Console->InputCodePage = CodePage;
758
759 return STATUS_SUCCESS;
760 }
761
762 /* EOF */