Sunc with trunk revision 58971.
[reactos.git] / win32ss / user / consrv / console.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: win32ss/user/consrv/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 #define COBJMACROS
14 #define NONAMELESSUNION
15
16 #include "consrv.h"
17 #include "include/conio.h"
18 #include "conio.h"
19 #include "handle.h"
20 #include "procinit.h"
21 #include "alias.h"
22 #include "coninput.h"
23 #include "conoutput.h"
24 #include "lineinput.h"
25 #include "include/settings.h"
26
27 #include "frontends/gui/guiterm.h"
28 #include "frontends/tui/tuiterm.h"
29
30 #include "include/console.h"
31 #include "console.h"
32 #include "resource.h"
33
34 #include <shlwapi.h>
35 #include <shlobj.h>
36
37 #define NDEBUG
38 #include <debug.h>
39
40 /* GLOBALS ********************************************************************/
41
42 static LIST_ENTRY ConsoleList; /* The list of all the allocated consoles */
43 static RTL_RESOURCE ListLock;
44
45 #define ConSrvLockConsoleListExclusive() \
46 RtlAcquireResourceExclusive(&ListLock, TRUE)
47
48 #define ConSrvLockConsoleListShared() \
49 RtlAcquireResourceShared(&ListLock, TRUE)
50
51 #define ConSrvUnlockConsoleList() \
52 RtlReleaseResource(&ListLock)
53
54 // Adapted from reactos/lib/rtl/unicode.c, RtlCreateUnicodeString line 2180
55 BOOLEAN
56 ConsoleCreateUnicodeString(IN OUT PUNICODE_STRING UniDest,
57 IN PCWSTR Source)
58 {
59 SIZE_T Size = (wcslen(Source) + 1) * sizeof(WCHAR);
60 if (Size > MAXUSHORT) return FALSE;
61
62 UniDest->Buffer = RtlAllocateHeap(ConSrvHeap, HEAP_ZERO_MEMORY, Size);
63 if (UniDest->Buffer == NULL) return FALSE;
64
65 RtlCopyMemory(UniDest->Buffer, Source, Size);
66 UniDest->MaximumLength = (USHORT)Size;
67 UniDest->Length = (USHORT)Size - sizeof(WCHAR);
68
69 return TRUE;
70 }
71
72 // Adapted from reactos/lib/rtl/unicode.c, RtlFreeUnicodeString line 431
73 VOID
74 ConsoleFreeUnicodeString(IN PUNICODE_STRING UnicodeString)
75 {
76 if (UnicodeString->Buffer)
77 {
78 RtlFreeHeap(ConSrvHeap, 0, UnicodeString->Buffer);
79 RtlZeroMemory(UnicodeString, sizeof(UNICODE_STRING));
80 }
81 }
82
83
84 /* PRIVATE FUNCTIONS **********************************************************/
85
86 static BOOL
87 DtbgIsDesktopVisible(VOID)
88 {
89 return !((BOOL)NtUserCallNoParam(NOPARAM_ROUTINE_ISCONSOLEMODE));
90 }
91
92 static ULONG
93 ConSrvConsoleCtrlEventTimeout(DWORD Event,
94 PCONSOLE_PROCESS_DATA ProcessData,
95 DWORD Timeout)
96 {
97 ULONG Status = ERROR_SUCCESS;
98
99 DPRINT("ConSrvConsoleCtrlEventTimeout Parent ProcessId = %x\n", ProcessData->Process->ClientId.UniqueProcess);
100
101 if (ProcessData->CtrlDispatcher)
102 {
103 _SEH2_TRY
104 {
105 HANDLE Thread = NULL;
106
107 _SEH2_TRY
108 {
109 Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0,
110 ProcessData->CtrlDispatcher,
111 UlongToPtr(Event), 0, NULL);
112 if (NULL == Thread)
113 {
114 Status = GetLastError();
115 DPRINT1("Failed thread creation (Error: 0x%x)\n", Status);
116 }
117 else
118 {
119 DPRINT("ProcessData->CtrlDispatcher remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n", ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process);
120 WaitForSingleObject(Thread, Timeout);
121 }
122 }
123 _SEH2_FINALLY
124 {
125 CloseHandle(Thread);
126 }
127 _SEH2_END;
128 }
129 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
130 {
131 Status = RtlNtStatusToDosError(_SEH2_GetExceptionCode());
132 DPRINT1("ConSrvConsoleCtrlEventTimeout - Caught an exception, Status = %08X\n", Status);
133 }
134 _SEH2_END;
135 }
136
137 return Status;
138 }
139
140 static ULONG
141 ConSrvConsoleCtrlEvent(DWORD Event,
142 PCONSOLE_PROCESS_DATA ProcessData)
143 {
144 return ConSrvConsoleCtrlEventTimeout(Event, ProcessData, 0);
145 }
146
147 ULONG FASTCALL
148 ConSrvConsoleProcessCtrlEvent(PCONSOLE Console,
149 ULONG ProcessGroupId,
150 DWORD Event)
151 {
152 ULONG Status = ERROR_SUCCESS;
153 PLIST_ENTRY current_entry;
154 PCONSOLE_PROCESS_DATA current;
155
156 /* If the console is already being destroyed, just return */
157 if (!ConSrvValidateConsole(Console, CONSOLE_RUNNING, FALSE))
158 return STATUS_UNSUCCESSFUL;
159
160 /*
161 * Loop through the process list, from the most recent process
162 * (the active one) to the oldest one (the first created, i.e.
163 * the console leader process), and for each, send an event
164 * (new processes are inserted at the head of the console process list).
165 */
166 current_entry = Console->ProcessList.Flink;
167 while (current_entry != &Console->ProcessList)
168 {
169 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
170 current_entry = current_entry->Flink;
171
172 /*
173 * Only processes belonging to the same process group are signaled.
174 * If the process group ID is zero, then all the processes are signaled.
175 */
176 if (ProcessGroupId == 0 || current->Process->ProcessGroupId == ProcessGroupId)
177 {
178 Status = ConSrvConsoleCtrlEvent(Event, current);
179 }
180 }
181
182 return Status;
183 }
184
185 VOID FASTCALL
186 ConioPause(PCONSOLE Console, UINT Flags)
187 {
188 Console->PauseFlags |= Flags;
189 if (!Console->UnpauseEvent)
190 Console->UnpauseEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
191 }
192
193 VOID FASTCALL
194 ConioUnpause(PCONSOLE Console, UINT Flags)
195 {
196 Console->PauseFlags &= ~Flags;
197
198 // if ((Console->PauseFlags & (PAUSED_FROM_KEYBOARD | PAUSED_FROM_SCROLLBAR | PAUSED_FROM_SELECTION)) == 0)
199 if (Console->PauseFlags == 0 && Console->UnpauseEvent)
200 {
201 SetEvent(Console->UnpauseEvent);
202 CloseHandle(Console->UnpauseEvent);
203 Console->UnpauseEvent = NULL;
204
205 CsrNotifyWait(&Console->WriteWaitQueue,
206 WaitAll,
207 NULL,
208 NULL);
209 if (!IsListEmpty(&Console->WriteWaitQueue))
210 {
211 CsrDereferenceWait(&Console->WriteWaitQueue);
212 }
213 }
214 }
215
216 BOOL FASTCALL
217 ConSrvValidateConsolePointer(PCONSOLE Console)
218 {
219 PLIST_ENTRY ConsoleEntry;
220 PCONSOLE CurrentConsole = NULL;
221
222 if (!Console) return FALSE;
223
224 /* The console list must be locked */
225 // ASSERT(Console_list_locked);
226
227 ConsoleEntry = ConsoleList.Flink;
228 while (ConsoleEntry != &ConsoleList)
229 {
230 CurrentConsole = CONTAINING_RECORD(ConsoleEntry, CONSOLE, Entry);
231 ConsoleEntry = ConsoleEntry->Flink;
232 if (CurrentConsole == Console) return TRUE;
233 }
234
235 return FALSE;
236 }
237
238 BOOL FASTCALL
239 ConSrvValidateConsoleState(PCONSOLE Console,
240 CONSOLE_STATE ExpectedState)
241 {
242 // if (!Console) return FALSE;
243
244 /* The console must be locked */
245 // ASSERT(Console_locked);
246
247 return (Console->State == ExpectedState);
248 }
249
250 BOOL FASTCALL
251 ConSrvValidateConsoleUnsafe(PCONSOLE Console,
252 CONSOLE_STATE ExpectedState,
253 BOOL LockConsole)
254 {
255 if (!Console) return FALSE;
256
257 /*
258 * Lock the console to forbid possible console's state changes
259 * (which must be done when the console is already locked).
260 * If we don't want to lock it, it's because the lock is already
261 * held. So there must be no problems.
262 */
263 if (LockConsole) EnterCriticalSection(&Console->Lock);
264
265 // ASSERT(Console_locked);
266
267 /* Check whether the console's state is what we expect */
268 if (!ConSrvValidateConsoleState(Console, ExpectedState))
269 {
270 if (LockConsole) LeaveCriticalSection(&Console->Lock);
271 return FALSE;
272 }
273
274 return TRUE;
275 }
276
277 BOOL FASTCALL
278 ConSrvValidateConsole(PCONSOLE Console,
279 CONSOLE_STATE ExpectedState,
280 BOOL LockConsole)
281 {
282 BOOL RetVal = FALSE;
283
284 if (!Console) return FALSE;
285
286 /*
287 * Forbid creation or deletion of consoles when
288 * checking for the existence of a console.
289 */
290 ConSrvLockConsoleListShared();
291
292 if (ConSrvValidateConsolePointer(Console))
293 {
294 RetVal = ConSrvValidateConsoleUnsafe(Console,
295 ExpectedState,
296 LockConsole);
297 }
298
299 /* Unlock the console list and return */
300 ConSrvUnlockConsoleList();
301 return RetVal;
302 }
303
304 NTSTATUS
305 FASTCALL
306 ConSrvGetConsole(PCONSOLE_PROCESS_DATA ProcessData,
307 PCONSOLE* Console,
308 BOOL LockConsole)
309 {
310 NTSTATUS Status = STATUS_SUCCESS;
311 PCONSOLE ProcessConsole;
312
313 RtlEnterCriticalSection(&ProcessData->HandleTableLock);
314 ProcessConsole = ProcessData->Console;
315
316 if (ConSrvValidateConsole(ProcessConsole, CONSOLE_RUNNING, LockConsole))
317 {
318 InterlockedIncrement(&ProcessConsole->ReferenceCount);
319 *Console = ProcessConsole;
320 }
321 else
322 {
323 *Console = NULL;
324 Status = STATUS_INVALID_HANDLE;
325 }
326
327 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
328 return Status;
329 }
330
331 VOID FASTCALL
332 ConSrvReleaseConsole(PCONSOLE Console,
333 BOOL WasConsoleLocked)
334 {
335 LONG RefCount = 0;
336
337 if (!Console) return;
338 // if (Console->ReferenceCount == 0) return; // This shouldn't happen
339 ASSERT(Console->ReferenceCount > 0);
340
341 /* The console must be locked */
342 // ASSERT(Console_locked);
343
344 /*
345 * Decrement the reference count. Save the new value too,
346 * because Console->ReferenceCount might be modified after
347 * the console gets unlocked but before we check whether we
348 * can destroy it.
349 */
350 RefCount = _InterlockedDecrement(&Console->ReferenceCount);
351
352 /* Unlock the console if needed */
353 if (WasConsoleLocked) LeaveCriticalSection(&Console->Lock);
354
355 /* Delete the console if needed */
356 if (RefCount <= 0) ConSrvDeleteConsole(Console);
357 }
358
359 VOID WINAPI
360 ConSrvInitConsoleSupport(VOID)
361 {
362 DPRINT("CONSRV: ConSrvInitConsoleSupport()\n");
363
364 /* Initialize the console list and its lock */
365 InitializeListHead(&ConsoleList);
366 RtlInitializeResource(&ListLock);
367
368 /* Should call LoadKeyboardLayout */
369 }
370
371 static BOOL
372 LoadShellLinkConsoleInfo(IN OUT PCONSOLE_START_INFO ConsoleStartInfo,
373 IN OUT PCONSOLE_INFO ConsoleInfo,
374 OUT LPWSTR IconPath,
375 IN SIZE_T IconPathLength,
376 OUT PINT piIcon)
377 {
378 #define PATH_SEPARATOR L'\\'
379
380 BOOL RetVal = FALSE;
381 HRESULT hRes = S_OK;
382 LPWSTR LinkName = NULL;
383 SIZE_T Length = 0;
384
385 if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
386 return FALSE;
387
388 if (IconPath == NULL || piIcon == NULL)
389 return FALSE;
390
391 IconPath[0] = L'\0';
392 *piIcon = 0;
393
394 /* 1- Find the last path separator if any */
395 LinkName = wcsrchr(ConsoleStartInfo->ConsoleTitle, PATH_SEPARATOR);
396 if (LinkName == NULL)
397 {
398 LinkName = ConsoleStartInfo->ConsoleTitle;
399 }
400 else
401 {
402 /* Skip the path separator */
403 ++LinkName;
404 }
405
406 /* 2- Check for the link extension. The name ".lnk" is considered invalid. */
407 Length = wcslen(LinkName);
408 if ( (Length <= 4) || (wcsicmp(LinkName + (Length - 4), L".lnk") != 0) )
409 return FALSE;
410
411 /* 3- It may be a link. Try to retrieve some properties */
412 hRes = CoInitialize(NULL);
413 if (SUCCEEDED(hRes))
414 {
415 /* Get a pointer to the IShellLink interface */
416 IShellLinkW* pshl = NULL;
417 hRes = CoCreateInstance(&CLSID_ShellLink,
418 NULL,
419 CLSCTX_INPROC_SERVER,
420 &IID_IShellLinkW,
421 (LPVOID*)&pshl);
422 if (SUCCEEDED(hRes))
423 {
424 /* Get a pointer to the IPersistFile interface */
425 IPersistFile* ppf = NULL;
426 hRes = IPersistFile_QueryInterface(pshl, &IID_IPersistFile, (LPVOID*)&ppf);
427 if (SUCCEEDED(hRes))
428 {
429 /* Load the shortcut */
430 hRes = IPersistFile_Load(ppf, ConsoleStartInfo->ConsoleTitle, STGM_READ);
431 if (SUCCEEDED(hRes))
432 {
433 /*
434 * Finally we can get the properties !
435 * Update the old ones if needed.
436 */
437 INT ShowCmd = 0;
438 // WORD HotKey = 0;
439
440 /* Reset the name of the console with the name of the shortcut */
441 Length = min(/*Length*/ Length - 4, // 4 == len(".lnk")
442 sizeof(ConsoleInfo->ConsoleTitle) / sizeof(ConsoleInfo->ConsoleTitle[0]) - 1);
443 wcsncpy(ConsoleInfo->ConsoleTitle, LinkName, Length);
444 ConsoleInfo->ConsoleTitle[Length] = L'\0';
445
446 /* Get the window showing command */
447 hRes = IShellLinkW_GetShowCmd(pshl, &ShowCmd);
448 if (SUCCEEDED(hRes)) ConsoleStartInfo->ShowWindow = (WORD)ShowCmd;
449
450 /* Get the hotkey */
451 // hRes = pshl->GetHotkey(&ShowCmd);
452 // if (SUCCEEDED(hRes)) ConsoleStartInfo->HotKey = HotKey;
453
454 /* Get the icon location, if any */
455 hRes = IShellLinkW_GetIconLocation(pshl, IconPath, IconPathLength, piIcon);
456 if (!SUCCEEDED(hRes))
457 {
458 IconPath[0] = L'\0';
459 }
460
461 // FIXME: Since we still don't load console properties from the shortcut,
462 // return false. When this will be done, we will return true instead.
463 RetVal = FALSE;
464 }
465 IPersistFile_Release(ppf);
466 }
467 IShellLinkW_Release(pshl);
468 }
469 }
470 CoUninitialize();
471
472 return RetVal;
473 }
474
475 NTSTATUS WINAPI
476 ConSrvInitConsole(OUT PCONSOLE* NewConsole,
477 IN OUT PCONSOLE_START_INFO ConsoleStartInfo,
478 IN PCSR_PROCESS ConsoleLeaderProcess)
479 {
480 NTSTATUS Status;
481 SECURITY_ATTRIBUTES SecurityAttributes;
482 CONSOLE_INFO ConsoleInfo;
483 SIZE_T Length = 0;
484 DWORD ProcessId = HandleToUlong(ConsoleLeaderProcess->ClientId.UniqueProcess);
485 PCONSOLE Console;
486 PCONSOLE_SCREEN_BUFFER NewBuffer;
487 BOOL GuiMode;
488 WCHAR Title[128];
489 WCHAR IconPath[MAX_PATH + 1] = L"";
490 INT iIcon = 0;
491
492 if (NewConsole == NULL) return STATUS_INVALID_PARAMETER;
493 *NewConsole = NULL;
494
495 /*
496 * Allocate a console structure
497 */
498 Console = RtlAllocateHeap(ConSrvHeap, HEAP_ZERO_MEMORY, sizeof(CONSOLE));
499 if (NULL == Console)
500 {
501 DPRINT1("Not enough memory for console creation.\n");
502 return STATUS_NO_MEMORY;
503 }
504
505 /*
506 * Load the console settings
507 */
508
509 /* 1. Load the default settings */
510 ConSrvGetDefaultSettings(&ConsoleInfo, ProcessId);
511
512 /* 2. Get the title of the console (initialize ConsoleInfo.ConsoleTitle) */
513 Length = min(wcslen(ConsoleStartInfo->ConsoleTitle),
514 sizeof(ConsoleInfo.ConsoleTitle) / sizeof(ConsoleInfo.ConsoleTitle[0]) - 1);
515 wcsncpy(ConsoleInfo.ConsoleTitle, ConsoleStartInfo->ConsoleTitle, Length);
516 ConsoleInfo.ConsoleTitle[Length] = L'\0';
517
518 /*
519 * 3. Check whether the process creating the console was launched
520 * via a shell-link. ConsoleInfo.ConsoleTitle may be updated by
521 * the name of the shortcut.
522 */
523 if (ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME)
524 {
525 if (!LoadShellLinkConsoleInfo(ConsoleStartInfo,
526 &ConsoleInfo,
527 IconPath,
528 MAX_PATH,
529 &iIcon))
530 {
531 ConsoleStartInfo->dwStartupFlags &= ~STARTF_TITLEISLINKNAME;
532 }
533 }
534
535 /*
536 * 4. Load the remaining console settings via the registry.
537 */
538 if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
539 {
540 /*
541 * Either we weren't created by an app launched via a shell-link,
542 * or we failed to load shell-link console properties.
543 * Therefore, load the console infos for the application from the registry.
544 */
545 ConSrvReadUserSettings(&ConsoleInfo, ProcessId);
546
547 /*
548 * Now, update them with the properties the user might gave to us
549 * via the STARTUPINFO structure before calling CreateProcess
550 * (and which was transmitted via the ConsoleStartInfo structure).
551 * We therefore overwrite the values read in the registry.
552 */
553 if (ConsoleStartInfo->dwStartupFlags & STARTF_USEFILLATTRIBUTE)
554 {
555 ConsoleInfo.ScreenAttrib = (USHORT)ConsoleStartInfo->FillAttribute;
556 }
557 if (ConsoleStartInfo->dwStartupFlags & STARTF_USECOUNTCHARS)
558 {
559 ConsoleInfo.ScreenBufferSize = ConsoleStartInfo->ScreenBufferSize;
560 }
561 if (ConsoleStartInfo->dwStartupFlags & STARTF_USESIZE)
562 {
563 // ConsoleInfo->ConsoleSize = ConsoleStartInfo->ConsoleWindowSize;
564 ConsoleInfo.ConsoleSize.X = (SHORT)ConsoleStartInfo->ConsoleWindowSize.cx;
565 ConsoleInfo.ConsoleSize.Y = (SHORT)ConsoleStartInfo->ConsoleWindowSize.cy;
566 }
567 /*
568 if (ConsoleStartInfo->dwStartupFlags & STARTF_RUNFULLSCREEN)
569 {
570 ConsoleInfo.FullScreen = TRUE;
571 }
572 */
573 }
574
575 /*
576 * Initialize the console
577 */
578 Console->State = CONSOLE_INITIALIZING;
579 InitializeCriticalSection(&Console->Lock);
580 Console->ReferenceCount = 0;
581 InitializeListHead(&Console->ProcessList);
582 memcpy(Console->Colors, ConsoleInfo.Colors, sizeof(ConsoleInfo.Colors));
583 Console->ConsoleSize = ConsoleInfo.ConsoleSize;
584
585 /*
586 * Initialize the input buffer
587 */
588 Console->InputBuffer.Header.Type = INPUT_BUFFER;
589 Console->InputBuffer.Header.Console = Console;
590
591 SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
592 SecurityAttributes.lpSecurityDescriptor = NULL;
593 SecurityAttributes.bInheritHandle = TRUE;
594 Console->InputBuffer.ActiveEvent = CreateEventW(&SecurityAttributes, TRUE, FALSE, NULL);
595 if (NULL == Console->InputBuffer.ActiveEvent)
596 {
597 DeleteCriticalSection(&Console->Lock);
598 RtlFreeHeap(ConSrvHeap, 0, Console);
599 return STATUS_UNSUCCESSFUL;
600 }
601
602 Console->InputBuffer.Mode = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
603 ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT;
604 Console->QuickEdit = ConsoleInfo.QuickEdit;
605 Console->InsertMode = ConsoleInfo.InsertMode;
606 InitializeListHead(&Console->InputBuffer.ReadWaitQueue);
607 InitializeListHead(&Console->InputBuffer.InputEvents);
608 Console->LineBuffer = NULL;
609 Console->CodePage = GetOEMCP();
610 Console->OutputCodePage = GetOEMCP();
611
612 /* Initialize a new screen buffer with default settings */
613 InitializeListHead(&Console->BufferList);
614 Status = ConSrvCreateScreenBuffer(Console,
615 &NewBuffer,
616 ConsoleInfo.ScreenBufferSize,
617 ConsoleInfo.ScreenAttrib,
618 ConsoleInfo.PopupAttrib,
619 (ConsoleInfo.FullScreen ? CONSOLE_FULLSCREEN_MODE
620 : CONSOLE_WINDOWED_MODE),
621 TRUE,
622 ConsoleInfo.CursorSize);
623 if (!NT_SUCCESS(Status))
624 {
625 DPRINT1("ConSrvCreateScreenBuffer: failed, Status = 0x%08lx\n", Status);
626 CloseHandle(Console->InputBuffer.ActiveEvent);
627 DeleteCriticalSection(&Console->Lock);
628 RtlFreeHeap(ConSrvHeap, 0, Console);
629 return Status;
630 }
631 /* Make the new screen buffer active */
632 Console->ActiveBuffer = NewBuffer;
633 InitializeListHead(&Console->WriteWaitQueue);
634
635 /*
636 * Initialize the history buffers
637 */
638 InitializeListHead(&Console->HistoryBuffers);
639 Console->HistoryBufferSize = ConsoleInfo.HistoryBufferSize;
640 Console->NumberOfHistoryBuffers = ConsoleInfo.NumberOfHistoryBuffers;
641 Console->HistoryNoDup = ConsoleInfo.HistoryNoDup;
642
643 /* Initialize the console title */
644 ConsoleCreateUnicodeString(&Console->OriginalTitle, ConsoleInfo.ConsoleTitle);
645 if (ConsoleInfo.ConsoleTitle[0] == L'\0')
646 {
647 if (LoadStringW(ConSrvDllInstance, IDS_CONSOLE_TITLE, Title, sizeof(Title) / sizeof(Title[0])))
648 {
649 ConsoleCreateUnicodeString(&Console->Title, Title);
650 }
651 else
652 {
653 ConsoleCreateUnicodeString(&Console->Title, L"ReactOS Console");
654 }
655 }
656 else
657 {
658 ConsoleCreateUnicodeString(&Console->Title, ConsoleInfo.ConsoleTitle);
659 }
660
661 /* Lock the console until its initialization is finished */
662 // EnterCriticalSection(&Console->Lock);
663
664 /*
665 * If we are not in GUI-mode, start the text-mode terminal emulator.
666 * If we fail, try to start the GUI-mode terminal emulator.
667 */
668 GuiMode = DtbgIsDesktopVisible();
669
670 if (!GuiMode)
671 {
672 DPRINT("CONSRV: Opening text-mode terminal emulator\n");
673 Status = TuiInitConsole(Console,
674 ConsoleStartInfo,
675 &ConsoleInfo,
676 ProcessId);
677 if (!NT_SUCCESS(Status))
678 {
679 DPRINT1("Failed to open text-mode terminal emulator, switching to gui-mode, Status = 0x%08lx\n", Status);
680 GuiMode = TRUE;
681 }
682 }
683
684 /*
685 * Try to open the GUI-mode terminal emulator. Two cases are possible:
686 * - We are in GUI-mode, therefore GuiMode == TRUE, the previous test-case
687 * failed and we start GUI-mode terminal emulator.
688 * - We are in text-mode, therefore GuiMode == FALSE, the previous test-case
689 * succeeded BUT we failed at starting text-mode terminal emulator.
690 * Then GuiMode was switched to TRUE in order to try to open the GUI-mode
691 * terminal emulator (Win32k will automatically switch to graphical mode,
692 * therefore no additional code is needed).
693 */
694 if (GuiMode)
695 {
696 DPRINT("CONSRV: Opening GUI-mode terminal emulator\n");
697 Status = GuiInitConsole(Console,
698 ConsoleStartInfo,
699 &ConsoleInfo,
700 ProcessId,
701 IconPath,
702 iIcon);
703 if (!NT_SUCCESS(Status))
704 {
705 DPRINT1("GuiInitConsole: failed, Status = 0x%08lx\n", Status);
706 ConsoleFreeUnicodeString(&Console->OriginalTitle);
707 ConsoleFreeUnicodeString(&Console->Title);
708 ConioDeleteScreenBuffer(NewBuffer);
709 CloseHandle(Console->InputBuffer.ActiveEvent);
710 // LeaveCriticalSection(&Console->Lock);
711 DeleteCriticalSection(&Console->Lock);
712 RtlFreeHeap(ConSrvHeap, 0, Console);
713 return Status;
714 }
715 }
716
717 DPRINT("Terminal initialized\n");
718
719 /* All went right, so add the console to the list */
720 ConSrvLockConsoleListExclusive();
721 DPRINT("Insert in the list\n");
722 InsertTailList(&ConsoleList, &Console->Entry);
723
724 /* The initialization is finished */
725 DPRINT("Change state\n");
726 Console->State = CONSOLE_RUNNING;
727
728 /* Unlock the console */
729 // LeaveCriticalSection(&Console->Lock);
730
731 /* Unlock the console list */
732 ConSrvUnlockConsoleList();
733
734 /* Copy buffer contents to screen */
735 ConioDrawConsole(Console);
736 DPRINT("Console drawn\n");
737
738 /* Return the newly created console to the caller and a success code too */
739 *NewConsole = Console;
740 return STATUS_SUCCESS;
741 }
742
743 VOID WINAPI
744 ConSrvDeleteConsole(PCONSOLE Console)
745 {
746 DPRINT("ConSrvDeleteConsole\n");
747
748 /*
749 * Forbid validation of any console by other threads
750 * during the deletion of this console.
751 */
752 ConSrvLockConsoleListExclusive();
753
754 /* Check the existence of the console, and if it's ok, continue */
755 if (!ConSrvValidateConsolePointer(Console))
756 {
757 /* Unlock the console list and return */
758 ConSrvUnlockConsoleList();
759 return;
760 }
761
762 /*
763 * If the console is already being destroyed
764 * (thus not running), just return.
765 */
766 if (!ConSrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
767 {
768 /* Unlock the console list and return */
769 ConSrvUnlockConsoleList();
770 return;
771 }
772
773 /*
774 * We are about to be destroyed. Signal it to other people
775 * so that they can terminate what they are doing, and that
776 * they cannot longer validate the console.
777 */
778 Console->State = CONSOLE_TERMINATING;
779
780 /*
781 * Allow other threads to finish their job: basically, unlock
782 * all other calls to EnterCriticalSection(&Console->Lock); by
783 * ConSrvValidateConsole(Unsafe) functions so that they just see
784 * that we are not in CONSOLE_RUNNING state anymore, or unlock
785 * other concurrent calls to ConSrvDeleteConsole so that they
786 * can see that we are in fact already deleting the console.
787 */
788 LeaveCriticalSection(&Console->Lock);
789 ConSrvUnlockConsoleList();
790
791 /* FIXME: Send a terminate message to all the processes owning this console */
792
793 /* Cleanup the UI-oriented part */
794 ConioCleanupConsole(Console);
795
796 /***
797 * Check that the console is in terminating state before continuing
798 * (the cleanup code must not change the state of the console...
799 * ...unless to cancel console deletion ?).
800 ***/
801
802 ConSrvLockConsoleListExclusive();
803
804 /* Re-check the existence of the console, and if it's ok, continue */
805 if (!ConSrvValidateConsolePointer(Console))
806 {
807 /* Unlock the console list and return */
808 ConSrvUnlockConsoleList();
809 return;
810 }
811
812 if (!ConSrvValidateConsoleUnsafe(Console, CONSOLE_TERMINATING, TRUE))
813 {
814 ConSrvUnlockConsoleList();
815 return;
816 }
817
818 /* We are in destruction */
819 Console->State = CONSOLE_IN_DESTRUCTION;
820
821 /* Remove the console from the list */
822 RemoveEntryList(&Console->Entry);
823
824 /* Reset the count to be sure */
825 Console->ReferenceCount = 0;
826
827 /* Discard all entries in the input event queue */
828 PurgeInputBuffer(Console);
829
830 if (Console->LineBuffer) RtlFreeHeap(ConSrvHeap, 0, Console->LineBuffer);
831
832 IntDeleteAllAliases(Console);
833 HistoryDeleteBuffers(Console);
834
835 ConioDeleteScreenBuffer(Console->ActiveBuffer);
836 if (!IsListEmpty(&Console->BufferList))
837 {
838 DPRINT1("BUG: screen buffer list not empty\n");
839 }
840
841 // CloseHandle(Console->InputBuffer.ActiveEvent);
842 if (Console->UnpauseEvent) CloseHandle(Console->UnpauseEvent);
843
844 ConsoleFreeUnicodeString(&Console->OriginalTitle);
845 ConsoleFreeUnicodeString(&Console->Title);
846
847 DPRINT("ConSrvDeleteConsole - Unlocking\n");
848 LeaveCriticalSection(&Console->Lock);
849 DPRINT("ConSrvDeleteConsole - Destroying lock\n");
850 DeleteCriticalSection(&Console->Lock);
851 DPRINT("ConSrvDeleteConsole - Lock destroyed ; freeing console\n");
852
853 RtlFreeHeap(ConSrvHeap, 0, Console);
854 DPRINT("ConSrvDeleteConsole - Console freed\n");
855
856 /* Unlock the console list and return */
857 ConSrvUnlockConsoleList();
858 }
859
860
861 /* PUBLIC SERVER APIS *********************************************************/
862
863 CSR_API(SrvAllocConsole)
864 {
865 NTSTATUS Status = STATUS_SUCCESS;
866 PCONSOLE_ALLOCCONSOLE AllocConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AllocConsoleRequest;
867 PCSR_PROCESS CsrProcess = CsrGetClientThread()->Process;
868 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrProcess);
869
870 if (ProcessData->Console != NULL)
871 {
872 DPRINT1("Process already has a console\n");
873 return STATUS_ACCESS_DENIED;
874 }
875
876 if (!CsrValidateMessageBuffer(ApiMessage,
877 (PVOID*)&AllocConsoleRequest->ConsoleStartInfo,
878 1,
879 sizeof(CONSOLE_START_INFO)))
880 {
881 return STATUS_INVALID_PARAMETER;
882 }
883
884 /*
885 * We are about to create a new console. However when ConSrvNewProcess
886 * was called, we didn't know that we wanted to create a new console and
887 * therefore, we by default inherited the handles table from our parent
888 * process. It's only now that we notice that in fact we do not need
889 * them, because we've created a new console and thus we must use it.
890 *
891 * Therefore, free the console we can have and our handles table,
892 * and recreate a new one later on.
893 */
894 ConSrvRemoveConsole(ProcessData);
895
896 /* Initialize a new Console owned by the Console Leader Process */
897 Status = ConSrvAllocateConsole(ProcessData,
898 &AllocConsoleRequest->InputHandle,
899 &AllocConsoleRequest->OutputHandle,
900 &AllocConsoleRequest->ErrorHandle,
901 AllocConsoleRequest->ConsoleStartInfo);
902 if (!NT_SUCCESS(Status))
903 {
904 DPRINT1("Console allocation failed\n");
905 return Status;
906 }
907
908 /* Return it to the caller */
909 AllocConsoleRequest->Console = ProcessData->Console;
910
911 /* Input Wait Handle */
912 AllocConsoleRequest->InputWaitHandle = ProcessData->ConsoleEvent;
913
914 /* Set the Property Dialog Handler */
915 ProcessData->PropDispatcher = AllocConsoleRequest->PropDispatcher;
916
917 /* Set the Ctrl Dispatcher */
918 ProcessData->CtrlDispatcher = AllocConsoleRequest->CtrlDispatcher;
919
920 return STATUS_SUCCESS;
921 }
922
923 CSR_API(SrvAttachConsole)
924 {
925 NTSTATUS Status = STATUS_SUCCESS;
926 PCONSOLE_ATTACHCONSOLE AttachConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AttachConsoleRequest;
927 PCSR_PROCESS SourceProcess = NULL; // The parent process.
928 PCSR_PROCESS TargetProcess = CsrGetClientThread()->Process; // Ourselves.
929 HANDLE ProcessId = ULongToHandle(AttachConsoleRequest->ProcessId);
930 PCONSOLE_PROCESS_DATA SourceProcessData, TargetProcessData;
931
932 TargetProcessData = ConsoleGetPerProcessData(TargetProcess);
933
934 if (TargetProcessData->Console != NULL)
935 {
936 DPRINT1("Process already has a console\n");
937 return STATUS_ACCESS_DENIED;
938 }
939
940 /* Check whether we try to attach to the parent's console */
941 if (ProcessId == ULongToHandle(ATTACH_PARENT_PROCESS))
942 {
943 PROCESS_BASIC_INFORMATION ProcessInfo;
944 ULONG Length = sizeof(ProcessInfo);
945
946 /* Get the real parent's ID */
947
948 Status = NtQueryInformationProcess(TargetProcess->ProcessHandle,
949 ProcessBasicInformation,
950 &ProcessInfo,
951 Length, &Length);
952 if (!NT_SUCCESS(Status))
953 {
954 DPRINT1("SrvAttachConsole - Cannot retrieve basic process info, Status = %lu\n", Status);
955 return Status;
956 }
957
958 ProcessId = ULongToHandle(ProcessInfo.InheritedFromUniqueProcessId);
959 }
960
961 /* Lock the source process via its PID */
962 Status = CsrLockProcessByClientId(ProcessId, &SourceProcess);
963 if (!NT_SUCCESS(Status)) return Status;
964
965 SourceProcessData = ConsoleGetPerProcessData(SourceProcess);
966
967 if (SourceProcessData->Console == NULL)
968 {
969 Status = STATUS_INVALID_HANDLE;
970 goto Quit;
971 }
972
973 /*
974 * We are about to create a new console. However when ConSrvNewProcess
975 * was called, we didn't know that we wanted to create a new console and
976 * therefore, we by default inherited the handles table from our parent
977 * process. It's only now that we notice that in fact we do not need
978 * them, because we've created a new console and thus we must use it.
979 *
980 * Therefore, free the console we can have and our handles table,
981 * and recreate a new one later on.
982 */
983 ConSrvRemoveConsole(TargetProcessData);
984
985 /*
986 * Inherit the console from the parent,
987 * if any, otherwise return an error.
988 */
989 Status = ConSrvInheritConsole(TargetProcessData,
990 SourceProcessData->Console,
991 TRUE,
992 &AttachConsoleRequest->InputHandle,
993 &AttachConsoleRequest->OutputHandle,
994 &AttachConsoleRequest->ErrorHandle);
995 if (!NT_SUCCESS(Status))
996 {
997 DPRINT1("Console inheritance failed\n");
998 goto Quit;
999 }
1000
1001 /* Return it to the caller */
1002 AttachConsoleRequest->Console = TargetProcessData->Console;
1003
1004 /* Input Wait Handle */
1005 AttachConsoleRequest->InputWaitHandle = TargetProcessData->ConsoleEvent;
1006
1007 /* Set the Property Dialog Handler */
1008 TargetProcessData->PropDispatcher = AttachConsoleRequest->PropDispatcher;
1009
1010 /* Set the Ctrl Dispatcher */
1011 TargetProcessData->CtrlDispatcher = AttachConsoleRequest->CtrlDispatcher;
1012
1013 Status = STATUS_SUCCESS;
1014
1015 Quit:
1016 /* Unlock the "source" process and exit */
1017 CsrUnlockProcess(SourceProcess);
1018 return Status;
1019 }
1020
1021 CSR_API(SrvFreeConsole)
1022 {
1023 ConSrvRemoveConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process));
1024 return STATUS_SUCCESS;
1025 }
1026
1027 CSR_API(SrvGetConsoleMode)
1028 {
1029 NTSTATUS Status;
1030 PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
1031 PCONSOLE_IO_OBJECT Object = NULL;
1032
1033 Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
1034 ConsoleModeRequest->ConsoleHandle,
1035 &Object, NULL, GENERIC_READ, TRUE, 0);
1036 if (!NT_SUCCESS(Status)) return Status;
1037
1038 Status = STATUS_SUCCESS;
1039
1040 if (INPUT_BUFFER == Object->Type)
1041 {
1042 PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
1043 PCONSOLE Console = InputBuffer->Header.Console;
1044 DWORD ConsoleMode = InputBuffer->Mode;
1045
1046 if (Console->QuickEdit || Console->InsertMode)
1047 {
1048 // Windows does this, even if it's not documented on MSDN
1049 ConsoleMode |= ENABLE_EXTENDED_FLAGS;
1050
1051 if (Console->QuickEdit ) ConsoleMode |= ENABLE_QUICK_EDIT_MODE;
1052 if (Console->InsertMode) ConsoleMode |= ENABLE_INSERT_MODE;
1053 }
1054
1055 ConsoleModeRequest->ConsoleMode = ConsoleMode;
1056 }
1057 else if (SCREEN_BUFFER == Object->Type)
1058 {
1059 PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
1060 ConsoleModeRequest->ConsoleMode = Buffer->Mode;
1061 }
1062 else
1063 {
1064 Status = STATUS_INVALID_HANDLE;
1065 }
1066
1067 ConSrvReleaseObject(Object, TRUE);
1068 return Status;
1069 }
1070
1071 CSR_API(SrvSetConsoleMode)
1072 {
1073 #define CONSOLE_VALID_CONTROL_MODES ( ENABLE_EXTENDED_FLAGS | ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE )
1074 #define CONSOLE_VALID_INPUT_MODES ( ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | \
1075 ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT | \
1076 ENABLE_MOUSE_INPUT )
1077 #define CONSOLE_VALID_OUTPUT_MODES ( ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT )
1078
1079 NTSTATUS Status;
1080 PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
1081 DWORD ConsoleMode = ConsoleModeRequest->ConsoleMode;
1082 PCONSOLE_IO_OBJECT Object = NULL;
1083
1084 Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
1085 ConsoleModeRequest->ConsoleHandle,
1086 &Object, NULL, GENERIC_WRITE, TRUE, 0);
1087 if (!NT_SUCCESS(Status)) return Status;
1088
1089 Status = STATUS_SUCCESS;
1090
1091 if (INPUT_BUFFER == Object->Type)
1092 {
1093 PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
1094 PCONSOLE Console = InputBuffer->Header.Console;
1095
1096 DPRINT("SetConsoleMode(Input, %d)\n", ConsoleMode);
1097
1098 /*
1099 * 1. Only the presence of valid mode flags is allowed.
1100 */
1101 if (ConsoleMode & ~(CONSOLE_VALID_INPUT_MODES | CONSOLE_VALID_CONTROL_MODES))
1102 {
1103 Status = STATUS_INVALID_PARAMETER;
1104 goto Quit;
1105 }
1106
1107 /*
1108 * 2. If we use control mode flags without ENABLE_EXTENDED_FLAGS,
1109 * then consider the flags invalid.
1110 *
1111 if ( (ConsoleMode & CONSOLE_VALID_CONTROL_MODES) &&
1112 (ConsoleMode & ENABLE_EXTENDED_FLAGS) == 0 )
1113 {
1114 Status = STATUS_INVALID_PARAMETER;
1115 goto Quit;
1116 }
1117 */
1118
1119 /*
1120 * 3. Now we can continue.
1121 */
1122 if (ConsoleMode & CONSOLE_VALID_CONTROL_MODES)
1123 {
1124 Console->QuickEdit = !!(ConsoleMode & ENABLE_QUICK_EDIT_MODE);
1125 Console->InsertMode = !!(ConsoleMode & ENABLE_INSERT_MODE);
1126 }
1127 InputBuffer->Mode = (ConsoleMode & CONSOLE_VALID_INPUT_MODES);
1128 }
1129 else if (SCREEN_BUFFER == Object->Type)
1130 {
1131 PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
1132
1133 DPRINT("SetConsoleMode(Output, %d)\n", ConsoleMode);
1134
1135 if (ConsoleMode & ~CONSOLE_VALID_OUTPUT_MODES)
1136 {
1137 Status = STATUS_INVALID_PARAMETER;
1138 }
1139 else
1140 {
1141 Buffer->Mode = (ConsoleMode & CONSOLE_VALID_OUTPUT_MODES);
1142 }
1143 }
1144 else
1145 {
1146 Status = STATUS_INVALID_HANDLE;
1147 }
1148
1149 Quit:
1150 ConSrvReleaseObject(Object, TRUE);
1151 return Status;
1152 }
1153
1154 CSR_API(SrvGetConsoleTitle)
1155 {
1156 NTSTATUS Status;
1157 PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
1158 // PCSR_PROCESS Process = CsrGetClientThread()->Process;
1159 PCONSOLE Console;
1160 DWORD Length;
1161
1162 if (!CsrValidateMessageBuffer(ApiMessage,
1163 (PVOID)&TitleRequest->Title,
1164 TitleRequest->Length,
1165 sizeof(BYTE)))
1166 {
1167 return STATUS_INVALID_PARAMETER;
1168 }
1169
1170 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1171 if (!NT_SUCCESS(Status))
1172 {
1173 DPRINT1("Can't get console\n");
1174 return Status;
1175 }
1176
1177 /* Copy title of the console to the user title buffer */
1178 if (TitleRequest->Length >= sizeof(WCHAR))
1179 {
1180 Length = min(TitleRequest->Length - sizeof(WCHAR), Console->Title.Length);
1181 memcpy(TitleRequest->Title, Console->Title.Buffer, Length);
1182 TitleRequest->Title[Length / sizeof(WCHAR)] = L'\0';
1183 }
1184
1185 TitleRequest->Length = Console->Title.Length;
1186
1187 ConSrvReleaseConsole(Console, TRUE);
1188 return STATUS_SUCCESS;
1189 }
1190
1191 CSR_API(SrvSetConsoleTitle)
1192 {
1193 NTSTATUS Status;
1194 PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
1195 // PCSR_PROCESS Process = CsrGetClientThread()->Process;
1196 PCONSOLE Console;
1197 PWCHAR Buffer;
1198
1199 if (!CsrValidateMessageBuffer(ApiMessage,
1200 (PVOID)&TitleRequest->Title,
1201 TitleRequest->Length,
1202 sizeof(BYTE)))
1203 {
1204 return STATUS_INVALID_PARAMETER;
1205 }
1206
1207 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1208 if (!NT_SUCCESS(Status))
1209 {
1210 DPRINT1("Can't get console\n");
1211 return Status;
1212 }
1213
1214 /* Allocate a new buffer to hold the new title (NULL-terminated) */
1215 Buffer = RtlAllocateHeap(ConSrvHeap, 0, TitleRequest->Length + sizeof(WCHAR));
1216 if (Buffer)
1217 {
1218 /* Free the old title */
1219 ConsoleFreeUnicodeString(&Console->Title);
1220
1221 /* Copy title to console */
1222 Console->Title.Buffer = Buffer;
1223 Console->Title.Length = TitleRequest->Length;
1224 Console->Title.MaximumLength = Console->Title.Length + sizeof(WCHAR);
1225 RtlCopyMemory(Console->Title.Buffer,
1226 TitleRequest->Title,
1227 Console->Title.Length);
1228 Console->Title.Buffer[Console->Title.Length / sizeof(WCHAR)] = L'\0';
1229
1230 ConioChangeTitle(Console);
1231 Status = STATUS_SUCCESS;
1232 }
1233 else
1234 {
1235 Status = STATUS_NO_MEMORY;
1236 }
1237
1238 ConSrvReleaseConsole(Console, TRUE);
1239 return Status;
1240 }
1241
1242 /**********************************************************************
1243 * HardwareStateProperty
1244 *
1245 * DESCRIPTION
1246 * Set/Get the value of the HardwareState and switch
1247 * between direct video buffer ouput and GDI windowed
1248 * output.
1249 * ARGUMENTS
1250 * Client hands us a CONSOLE_GETSETHWSTATE object.
1251 * We use the same object to Request.
1252 * NOTE
1253 * ConsoleHwState has the correct size to be compatible
1254 * with NT's, but values are not.
1255 */
1256 static NTSTATUS FASTCALL
1257 SetConsoleHardwareState(PCONSOLE Console, ULONG ConsoleHwState)
1258 {
1259 DPRINT1("Console Hardware State: %d\n", ConsoleHwState);
1260
1261 if ((CONSOLE_HARDWARE_STATE_GDI_MANAGED == ConsoleHwState)
1262 ||(CONSOLE_HARDWARE_STATE_DIRECT == ConsoleHwState))
1263 {
1264 if (Console->HardwareState != ConsoleHwState)
1265 {
1266 /* TODO: implement switching from full screen to windowed mode */
1267 /* TODO: or back; now simply store the hardware state */
1268 Console->HardwareState = ConsoleHwState;
1269 }
1270
1271 return STATUS_SUCCESS;
1272 }
1273
1274 return STATUS_INVALID_PARAMETER_3; /* Client: (handle, set_get, [mode]) */
1275 }
1276
1277 CSR_API(SrvGetConsoleHardwareState)
1278 {
1279 NTSTATUS Status;
1280 PCONSOLE_GETSETHWSTATE HardwareStateRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.HardwareStateRequest;
1281 PCONSOLE_SCREEN_BUFFER Buff;
1282 PCONSOLE Console;
1283
1284 Status = ConSrvGetScreenBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
1285 HardwareStateRequest->OutputHandle,
1286 &Buff,
1287 GENERIC_READ,
1288 TRUE);
1289 if (!NT_SUCCESS(Status))
1290 {
1291 DPRINT1("Failed to get console handle in SrvGetConsoleHardwareState\n");
1292 return Status;
1293 }
1294
1295 Console = Buff->Header.Console;
1296 HardwareStateRequest->State = Console->HardwareState;
1297
1298 ConSrvReleaseScreenBuffer(Buff, TRUE);
1299 return Status;
1300 }
1301
1302 CSR_API(SrvSetConsoleHardwareState)
1303 {
1304 NTSTATUS Status;
1305 PCONSOLE_GETSETHWSTATE HardwareStateRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.HardwareStateRequest;
1306 PCONSOLE_SCREEN_BUFFER Buff;
1307 PCONSOLE Console;
1308
1309 Status = ConSrvGetScreenBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
1310 HardwareStateRequest->OutputHandle,
1311 &Buff,
1312 GENERIC_WRITE,
1313 TRUE);
1314 if (!NT_SUCCESS(Status))
1315 {
1316 DPRINT1("Failed to get console handle in SrvSetConsoleHardwareState\n");
1317 return Status;
1318 }
1319
1320 DPRINT("Setting console hardware state.\n");
1321 Console = Buff->Header.Console;
1322 Status = SetConsoleHardwareState(Console, HardwareStateRequest->State);
1323
1324 ConSrvReleaseScreenBuffer(Buff, TRUE);
1325 return Status;
1326 }
1327
1328 CSR_API(SrvGetConsoleDisplayMode)
1329 {
1330 NTSTATUS Status;
1331 PCONSOLE_GETDISPLAYMODE GetDisplayModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetDisplayModeRequest;
1332 PCONSOLE Console;
1333 ULONG DisplayMode = 0;
1334
1335 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
1336 &Console, TRUE);
1337 if (!NT_SUCCESS(Status))
1338 {
1339 DPRINT1("Failed to get console handle in SrvGetConsoleDisplayMode\n");
1340 return Status;
1341 }
1342
1343 if (Console->ActiveBuffer->DisplayMode & CONSOLE_FULLSCREEN_MODE)
1344 DisplayMode |= CONSOLE_FULLSCREEN_HARDWARE; // CONSOLE_FULLSCREEN
1345 else if (Console->ActiveBuffer->DisplayMode & CONSOLE_WINDOWED_MODE)
1346 DisplayMode |= CONSOLE_WINDOWED;
1347
1348 GetDisplayModeRequest->DisplayMode = DisplayMode;
1349 Status = STATUS_SUCCESS;
1350
1351 ConSrvReleaseConsole(Console, TRUE);
1352 return Status;
1353 }
1354
1355 CSR_API(SrvSetConsoleDisplayMode)
1356 {
1357 NTSTATUS Status;
1358 PCONSOLE_SETDISPLAYMODE SetDisplayModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetDisplayModeRequest;
1359 PCONSOLE_SCREEN_BUFFER Buff;
1360
1361 Status = ConSrvGetScreenBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
1362 SetDisplayModeRequest->OutputHandle,
1363 &Buff,
1364 GENERIC_WRITE,
1365 TRUE);
1366 if (!NT_SUCCESS(Status))
1367 {
1368 DPRINT1("Failed to get console handle in SrvSetConsoleDisplayMode\n");
1369 return Status;
1370 }
1371
1372 if (SetDisplayModeRequest->DisplayMode & ~(CONSOLE_FULLSCREEN_MODE | CONSOLE_WINDOWED_MODE))
1373 {
1374 Status = STATUS_INVALID_PARAMETER;
1375 }
1376 else
1377 {
1378 Buff->DisplayMode = SetDisplayModeRequest->DisplayMode;
1379 // TODO: Change the display mode
1380 SetDisplayModeRequest->NewSBDim = Buff->ScreenBufferSize;
1381
1382 Status = STATUS_SUCCESS;
1383 }
1384
1385 ConSrvReleaseScreenBuffer(Buff, TRUE);
1386 return Status;
1387 }
1388
1389 CSR_API(SrvGetLargestConsoleWindowSize)
1390 {
1391 NTSTATUS Status;
1392 PCONSOLE_GETLARGESTWINDOWSIZE GetLargestWindowSizeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetLargestWindowSizeRequest;
1393 PCONSOLE_SCREEN_BUFFER Buff;
1394 PCONSOLE Console;
1395
1396 Status = ConSrvGetScreenBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
1397 GetLargestWindowSizeRequest->OutputHandle,
1398 &Buff,
1399 GENERIC_READ,
1400 TRUE);
1401 if (!NT_SUCCESS(Status)) return Status;
1402
1403 Console = Buff->Header.Console;
1404 ConioGetLargestConsoleWindowSize(Console, &GetLargestWindowSizeRequest->Size);
1405
1406 ConSrvReleaseScreenBuffer(Buff, TRUE);
1407 return STATUS_SUCCESS;
1408 }
1409
1410 CSR_API(SrvSetConsoleWindowInfo)
1411 {
1412 #if 0
1413 NTSTATUS Status;
1414 #endif
1415 PCONSOLE_SETWINDOWINFO SetWindowInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetWindowInfoRequest;
1416 #if 0
1417 PCONSOLE_SCREEN_BUFFER Buff;
1418 PCONSOLE Console;
1419 #endif
1420 SMALL_RECT WindowRect = SetWindowInfoRequest->WindowRect;
1421
1422 #if 0
1423 Status = ConSrvGetScreenBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
1424 SetWindowInfoRequest->OutputHandle,
1425 &Buff,
1426 GENERIC_READ,
1427 TRUE);
1428 if (!NT_SUCCESS(Status)) return Status;
1429
1430 Console = Buff->Header.Console;
1431
1432 if (SetWindowInfoRequest->Absolute == FALSE)
1433 {
1434 /* Relative positions given. Transform them to absolute ones */
1435 WindowRect.Left += Buff->ShowX;
1436 WindowRect.Top += Buff->ShowY;
1437 WindowRect.Right += Buff->ShowX + Console->ConsoleSize.X - 1;
1438 WindowRect.Bottom += Buff->ShowY + Console->ConsoleSize.Y - 1;
1439 }
1440
1441 if ( (WindowRect.Left < 0) || (WindowRect.Top < 0) ||
1442 (WindowRect.Right > ScreenBufferSize.X) ||
1443 (WindowRect.Bottom > ScreenBufferSize.Y) ||
1444 (WindowRect.Right <= WindowRect.Left) ||
1445 (WindowRect.Bottom <= WindowRect.Top) )
1446 {
1447 ConSrvReleaseScreenBuffer(Buff, TRUE);
1448 return STATUS_INVALID_PARAMETER;
1449 }
1450
1451 Buff->ShowX = WindowRect.Left;
1452 Buff->ShowY = WindowRect.Top;
1453
1454 // These two lines are frontend-specific.
1455 Console->ConsoleSize.X = WindowRect.Right - WindowRect.Left + 1;
1456 Console->ConsoleSize.Y = WindowRect.Bottom - WindowRect.Top + 1;
1457
1458 // ConioGetLargestConsoleWindowSize(Console, &GetLargestWindowSizeRequest->Size);
1459
1460 ConSrvReleaseScreenBuffer(Buff, TRUE);
1461 return STATUS_SUCCESS;
1462 #else
1463 DPRINT1("SrvSetConsoleWindowInfo(0x%08x, %d, {L%d, T%d, R%d, B%d}) UNIMPLEMENTED\n",
1464 SetWindowInfoRequest->OutputHandle, SetWindowInfoRequest->Absolute,
1465 WindowRect.Left, WindowRect.Top, WindowRect.Right, WindowRect.Bottom);
1466 return STATUS_NOT_IMPLEMENTED;
1467 #endif
1468 }
1469
1470 CSR_API(SrvGetConsoleWindow)
1471 {
1472 NTSTATUS Status;
1473 PCONSOLE_GETWINDOW GetWindowRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetWindowRequest;
1474 PCONSOLE Console;
1475
1476 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1477 if (!NT_SUCCESS(Status)) return Status;
1478
1479 GetWindowRequest->WindowHandle = ConioGetConsoleWindowHandle(Console);
1480
1481 ConSrvReleaseConsole(Console, TRUE);
1482 return STATUS_SUCCESS;
1483 }
1484
1485 CSR_API(SrvSetConsoleIcon)
1486 {
1487 NTSTATUS Status;
1488 PCONSOLE_SETICON SetIconRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetIconRequest;
1489 PCONSOLE Console;
1490
1491 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1492 if (!NT_SUCCESS(Status)) return Status;
1493
1494 Status = (ConioChangeIcon(Console, SetIconRequest->WindowIcon)
1495 ? STATUS_SUCCESS
1496 : STATUS_UNSUCCESSFUL);
1497
1498 ConSrvReleaseConsole(Console, TRUE);
1499 return Status;
1500 }
1501
1502 CSR_API(SrvGetConsoleCP)
1503 {
1504 NTSTATUS Status;
1505 PCONSOLE_GETSETINPUTOUTPUTCP ConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleCPRequest;
1506 PCONSOLE Console;
1507
1508 DPRINT("SrvGetConsoleCP, getting %s Code Page\n",
1509 ConsoleCPRequest->InputCP ? "Input" : "Output");
1510
1511 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1512 if (!NT_SUCCESS(Status)) return Status;
1513
1514 ConsoleCPRequest->CodePage = (ConsoleCPRequest->InputCP ? Console->CodePage
1515 : Console->OutputCodePage);
1516 ConSrvReleaseConsole(Console, TRUE);
1517 return STATUS_SUCCESS;
1518 }
1519
1520 CSR_API(SrvSetConsoleCP)
1521 {
1522 NTSTATUS Status;
1523 PCONSOLE_GETSETINPUTOUTPUTCP ConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleCPRequest;
1524 PCONSOLE Console;
1525
1526 DPRINT("SrvSetConsoleCP, setting %s Code Page\n",
1527 ConsoleCPRequest->InputCP ? "Input" : "Output");
1528
1529 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1530 if (!NT_SUCCESS(Status)) return Status;
1531
1532 if (IsValidCodePage(ConsoleCPRequest->CodePage))
1533 {
1534 if (ConsoleCPRequest->InputCP)
1535 Console->CodePage = ConsoleCPRequest->CodePage;
1536 else
1537 Console->OutputCodePage = ConsoleCPRequest->CodePage;
1538
1539 ConSrvReleaseConsole(Console, TRUE);
1540 return STATUS_SUCCESS;
1541 }
1542
1543 ConSrvReleaseConsole(Console, TRUE);
1544 return STATUS_INVALID_PARAMETER;
1545 }
1546
1547 CSR_API(SrvGetConsoleProcessList)
1548 {
1549 NTSTATUS Status;
1550 PCONSOLE_GETPROCESSLIST GetProcessListRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetProcessListRequest;
1551 PDWORD Buffer;
1552 // PCSR_PROCESS Process = CsrGetClientThread()->Process;
1553 PCONSOLE Console;
1554 PCONSOLE_PROCESS_DATA current;
1555 PLIST_ENTRY current_entry;
1556 ULONG nItems = 0;
1557
1558 if (!CsrValidateMessageBuffer(ApiMessage,
1559 (PVOID)&GetProcessListRequest->pProcessIds,
1560 GetProcessListRequest->nMaxIds,
1561 sizeof(DWORD)))
1562 {
1563 return STATUS_INVALID_PARAMETER;
1564 }
1565
1566 Buffer = GetProcessListRequest->pProcessIds;
1567
1568 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1569 if (!NT_SUCCESS(Status)) return Status;
1570
1571 for (current_entry = Console->ProcessList.Flink;
1572 current_entry != &Console->ProcessList;
1573 current_entry = current_entry->Flink)
1574 {
1575 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
1576 if (++nItems <= GetProcessListRequest->nMaxIds)
1577 {
1578 *Buffer++ = HandleToUlong(current->Process->ClientId.UniqueProcess);
1579 }
1580 }
1581
1582 ConSrvReleaseConsole(Console, TRUE);
1583
1584 GetProcessListRequest->nProcessIdsTotal = nItems;
1585 return STATUS_SUCCESS;
1586 }
1587
1588 CSR_API(SrvGenerateConsoleCtrlEvent)
1589 {
1590 NTSTATUS Status;
1591 PCONSOLE_GENERATECTRLEVENT GenerateCtrlEventRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GenerateCtrlEventRequest;
1592 PCONSOLE Console;
1593
1594 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1595 if (!NT_SUCCESS(Status)) return Status;
1596
1597 Status = ConSrvConsoleProcessCtrlEvent(Console,
1598 GenerateCtrlEventRequest->ProcessGroup,
1599 GenerateCtrlEventRequest->Event);
1600
1601 ConSrvReleaseConsole(Console, TRUE);
1602 return Status;
1603 }
1604
1605 CSR_API(SrvGetConsoleSelectionInfo)
1606 {
1607 NTSTATUS Status;
1608 PCONSOLE_GETSELECTIONINFO GetSelectionInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetSelectionInfoRequest;
1609 PCONSOLE Console;
1610
1611 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1612 if (NT_SUCCESS(Status))
1613 {
1614 memset(&GetSelectionInfoRequest->Info, 0, sizeof(CONSOLE_SELECTION_INFO));
1615 if (Console->Selection.dwFlags != 0)
1616 GetSelectionInfoRequest->Info = Console->Selection;
1617 ConSrvReleaseConsole(Console, TRUE);
1618 }
1619
1620 return Status;
1621 }
1622
1623 /* EOF */