2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: win32ss/user/winsrv/consrv/console.c
5 * PURPOSE: Console Management Functions
6 * PROGRAMMERS: Gé van Geldorp
8 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
11 /* INCLUDES *******************************************************************/
14 #include "include/conio.h"
15 #include "include/conio2.h"
20 #include "conoutput.h"
21 #include "lineinput.h"
22 #include "include/settings.h"
24 #include "frontends/gui/guiterm.h"
25 #ifdef TUITERM_COMPILE
26 #include "frontends/tui/tuiterm.h"
29 #include "include/console.h"
37 /* GLOBALS ********************************************************************/
40 #ifdef TUITERM_COMPILE
42 TuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd
,
43 IN OUT PCONSOLE_INFO ConsoleInfo
,
44 IN OUT PVOID ExtraConsoleInfo
,
47 TuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd
);
51 GuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd
,
52 IN OUT PCONSOLE_INFO ConsoleInfo
,
53 IN OUT PVOID ExtraConsoleInfo
,
56 GuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd
);
60 NTSTATUS (NTAPI
*FRONTEND_LOAD
)(IN OUT PFRONTEND FrontEnd
,
61 IN OUT PCONSOLE_INFO ConsoleInfo
,
62 IN OUT PVOID ExtraConsoleInfo
,
66 NTSTATUS (NTAPI
*FRONTEND_UNLOAD
)(IN OUT PFRONTEND FrontEnd
);
69 * If we are not in GUI-mode, start the text-mode terminal emulator.
70 * If we fail, try to start the GUI-mode terminal emulator.
72 * Try to open the GUI-mode terminal emulator. Two cases are possible:
73 * - We are in GUI-mode, therefore GuiMode == TRUE, the previous test-case
74 * failed and we start GUI-mode terminal emulator.
75 * - We are in text-mode, therefore GuiMode == FALSE, the previous test-case
76 * succeeded BUT we failed at starting text-mode terminal emulator.
77 * Then GuiMode was switched to TRUE in order to try to open the GUI-mode
78 * terminal emulator (Win32k will automatically switch to graphical mode,
79 * therefore no additional code is needed).
83 * NOTE: Each entry of the table should be retrieved when loading a front-end
84 * (examples of the CSR servers which register some data for CSRSS).
88 CHAR FrontEndName
[80];
89 FRONTEND_LOAD FrontEndLoad
;
90 FRONTEND_UNLOAD FrontEndUnload
;
91 } FrontEndLoadingMethods
[] =
93 #ifdef TUITERM_COMPILE
94 {"TUI", TuiLoadFrontEnd
, TuiUnloadFrontEnd
},
96 {"GUI", GuiLoadFrontEnd
, GuiUnloadFrontEnd
},
98 // {"Not found", 0, NULL}
102 /* PRIVATE FUNCTIONS **********************************************************/
106 ConioPause(PCONSOLE Console
, UINT Flags
)
108 Console
->PauseFlags
|= Flags
;
109 if (!Console
->UnpauseEvent
)
110 Console
->UnpauseEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
114 ConioUnpause(PCONSOLE Console
, UINT Flags
)
116 Console
->PauseFlags
&= ~Flags
;
118 // if ((Console->PauseFlags & (PAUSED_FROM_KEYBOARD | PAUSED_FROM_SCROLLBAR | PAUSED_FROM_SELECTION)) == 0)
119 if (Console
->PauseFlags
== 0 && Console
->UnpauseEvent
)
121 SetEvent(Console
->UnpauseEvent
);
122 CloseHandle(Console
->UnpauseEvent
);
123 Console
->UnpauseEvent
= NULL
;
125 CsrNotifyWait(&Console
->WriteWaitQueue
,
129 if (!IsListEmpty(&Console
->WriteWaitQueue
))
131 CsrDereferenceWait(&Console
->WriteWaitQueue
);
139 ConSrvGetConsole(PCONSOLE_PROCESS_DATA ProcessData
,
143 NTSTATUS Status
= STATUS_SUCCESS
;
144 PCONSOLE ProcessConsole
;
149 // RtlEnterCriticalSection(&ProcessData->HandleTableLock);
151 Status
= ConDrvGetConsole(&ProcessConsole
, ProcessData
->ConsoleHandle
, LockConsole
);
152 if (NT_SUCCESS(Status
)) *Console
= ProcessConsole
;
154 // RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
159 ConSrvReleaseConsole(PCONSOLE Console
,
160 BOOL WasConsoleLocked
)
162 /* Just call the driver*/
163 ConDrvReleaseConsole(Console
, WasConsoleLocked
);
168 ConSrvInitConsole(OUT PHANDLE NewConsoleHandle
,
169 OUT PCONSOLE
* NewConsole
,
170 IN OUT PCONSOLE_START_INFO ConsoleStartInfo
,
171 IN ULONG ConsoleLeaderProcessId
)
174 HANDLE ConsoleHandle
;
176 CONSOLE_INFO ConsoleInfo
;
181 if (NewConsole
== NULL
|| ConsoleStartInfo
== NULL
)
182 return STATUS_INVALID_PARAMETER
;
187 * Load the console settings
190 /* 1. Load the default settings */
191 ConSrvGetDefaultSettings(&ConsoleInfo
, ConsoleLeaderProcessId
);
193 /* 2. Get the title of the console (initialize ConsoleInfo.ConsoleTitle) */
194 Length
= min(wcslen(ConsoleStartInfo
->ConsoleTitle
),
195 sizeof(ConsoleInfo
.ConsoleTitle
) / sizeof(ConsoleInfo
.ConsoleTitle
[0]) - 1);
196 wcsncpy(ConsoleInfo
.ConsoleTitle
, ConsoleStartInfo
->ConsoleTitle
, Length
);
197 ConsoleInfo
.ConsoleTitle
[Length
] = L
'\0';
201 * Choose an adequate terminal front-end to load, and load it
203 Status
= STATUS_SUCCESS
;
204 for (i
= 0; i
< sizeof(FrontEndLoadingMethods
) / sizeof(FrontEndLoadingMethods
[0]); ++i
)
206 DPRINT("CONSRV: Trying to load %s terminal emulator...\n", FrontEndLoadingMethods
[i
].FrontEndName
);
207 Status
= FrontEndLoadingMethods
[i
].FrontEndLoad(&FrontEnd
,
210 ConsoleLeaderProcessId
);
211 if (NT_SUCCESS(Status
))
213 DPRINT("CONSRV: %s terminal emulator loaded successfully\n", FrontEndLoadingMethods
[i
].FrontEndName
);
218 DPRINT1("CONSRV: Loading %s terminal emulator failed, Status = 0x%08lx , continuing...\n", FrontEndLoadingMethods
[i
].FrontEndName
, Status
);
222 if (!NT_SUCCESS(Status
))
224 DPRINT1("CONSRV: Failed to initialize a frontend, Status = 0x%08lx\n", Status
);
228 DPRINT("CONSRV: Frontend initialized\n");
231 /******************************************************************************/
233 * 4. Load the remaining console settings via the registry.
235 if ((ConsoleStartInfo
->dwStartupFlags
& STARTF_TITLEISLINKNAME
) == 0)
238 * Either we weren't created by an app launched via a shell-link,
239 * or we failed to load shell-link console properties.
240 * Therefore, load the console infos for the application from the registry.
242 ConSrvReadUserSettings(&ConsoleInfo
, ConsoleLeaderProcessId
);
245 * Now, update them with the properties the user might gave to us
246 * via the STARTUPINFO structure before calling CreateProcess
247 * (and which was transmitted via the ConsoleStartInfo structure).
248 * We therefore overwrite the values read in the registry.
250 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_USEFILLATTRIBUTE
)
252 ConsoleInfo
.ScreenAttrib
= (USHORT
)ConsoleStartInfo
->FillAttribute
;
254 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_USECOUNTCHARS
)
256 ConsoleInfo
.ScreenBufferSize
= ConsoleStartInfo
->ScreenBufferSize
;
258 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_USESIZE
)
260 // ConsoleInfo.ConsoleSize = ConsoleStartInfo->ConsoleWindowSize;
261 ConsoleInfo
.ConsoleSize
.X
= (SHORT
)ConsoleStartInfo
->ConsoleWindowSize
.cx
;
262 ConsoleInfo
.ConsoleSize
.Y
= (SHORT
)ConsoleStartInfo
->ConsoleWindowSize
.cy
;
266 /* Set-up the code page */
267 ConsoleInfo
.CodePage
= GetOEMCP();
268 /******************************************************************************/
270 Status
= ConDrvInitConsole(&ConsoleHandle
,
273 ConsoleLeaderProcessId
);
274 if (!NT_SUCCESS(Status
))
276 DPRINT1("Creating a new console failed, Status = 0x%08lx\n", Status
);
277 FrontEndLoadingMethods
[i
].FrontEndUnload(&FrontEnd
);
282 DPRINT("Console initialized\n");
284 Status
= ConDrvRegisterFrontEnd(Console
, &FrontEnd
);
285 if (!NT_SUCCESS(Status
))
287 DPRINT1("Failed to register frontend to the given console, Status = 0x%08lx\n", Status
);
288 ConDrvDeleteConsole(Console
);
289 FrontEndLoadingMethods
[i
].FrontEndUnload(&FrontEnd
);
292 DPRINT("FrontEnd registered\n");
294 /* Return the newly created console to the caller and a success code too */
295 *NewConsoleHandle
= ConsoleHandle
;
296 *NewConsole
= Console
;
297 return STATUS_SUCCESS
;
301 ConSrvDeleteConsole(PCONSOLE Console
)
303 DPRINT("ConSrvDeleteConsole\n");
305 /* Just call the driver. ConSrvDeregisterFrontEnd is called on-demand. */
306 ConDrvDeleteConsole(Console
);
310 /* PUBLIC SERVER APIS *********************************************************/
312 CSR_API(SrvAllocConsole
)
314 NTSTATUS Status
= STATUS_SUCCESS
;
315 PCONSOLE_ALLOCCONSOLE AllocConsoleRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.AllocConsoleRequest
;
316 PCSR_PROCESS CsrProcess
= CsrGetClientThread()->Process
;
317 PCONSOLE_PROCESS_DATA ProcessData
= ConsoleGetPerProcessData(CsrProcess
);
319 if (ProcessData
->ConsoleHandle
!= NULL
)
321 DPRINT1("Process already has a console\n");
322 return STATUS_ACCESS_DENIED
;
325 if (!CsrValidateMessageBuffer(ApiMessage
,
326 (PVOID
*)&AllocConsoleRequest
->ConsoleStartInfo
,
328 sizeof(CONSOLE_START_INFO
)))
330 return STATUS_INVALID_PARAMETER
;
333 /* Initialize a new Console owned by the Console Leader Process */
334 Status
= ConSrvAllocateConsole(ProcessData
,
335 &AllocConsoleRequest
->InputHandle
,
336 &AllocConsoleRequest
->OutputHandle
,
337 &AllocConsoleRequest
->ErrorHandle
,
338 AllocConsoleRequest
->ConsoleStartInfo
);
339 if (!NT_SUCCESS(Status
))
341 DPRINT1("Console allocation failed\n");
345 /* Return the console handle and the input wait handle to the caller */
346 AllocConsoleRequest
->ConsoleHandle
= ProcessData
->ConsoleHandle
;
347 AllocConsoleRequest
->InputWaitHandle
= ProcessData
->ConsoleEvent
;
349 /* Set the Property-Dialog and Control-Dispatcher handlers */
350 ProcessData
->PropDispatcher
= AllocConsoleRequest
->PropDispatcher
;
351 ProcessData
->CtrlDispatcher
= AllocConsoleRequest
->CtrlDispatcher
;
353 return STATUS_SUCCESS
;
356 CSR_API(SrvAttachConsole
)
358 NTSTATUS Status
= STATUS_SUCCESS
;
359 PCONSOLE_ATTACHCONSOLE AttachConsoleRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.AttachConsoleRequest
;
360 PCSR_PROCESS SourceProcess
= NULL
; // The parent process.
361 PCSR_PROCESS TargetProcess
= CsrGetClientThread()->Process
; // Ourselves.
362 HANDLE ProcessId
= ULongToHandle(AttachConsoleRequest
->ProcessId
);
363 PCONSOLE_PROCESS_DATA SourceProcessData
, TargetProcessData
;
365 TargetProcessData
= ConsoleGetPerProcessData(TargetProcess
);
367 if (TargetProcessData
->ConsoleHandle
!= NULL
)
369 DPRINT1("Process already has a console\n");
370 return STATUS_ACCESS_DENIED
;
373 /* Check whether we try to attach to the parent's console */
374 if (ProcessId
== ULongToHandle(ATTACH_PARENT_PROCESS
))
376 PROCESS_BASIC_INFORMATION ProcessInfo
;
377 ULONG Length
= sizeof(ProcessInfo
);
379 /* Get the real parent's ID */
381 Status
= NtQueryInformationProcess(TargetProcess
->ProcessHandle
,
382 ProcessBasicInformation
,
385 if (!NT_SUCCESS(Status
))
387 DPRINT1("SrvAttachConsole - Cannot retrieve basic process info, Status = %lu\n", Status
);
391 ProcessId
= ULongToHandle(ProcessInfo
.InheritedFromUniqueProcessId
);
394 /* Lock the source process via its PID */
395 Status
= CsrLockProcessByClientId(ProcessId
, &SourceProcess
);
396 if (!NT_SUCCESS(Status
)) return Status
;
398 SourceProcessData
= ConsoleGetPerProcessData(SourceProcess
);
400 if (SourceProcessData
->ConsoleHandle
== NULL
)
402 Status
= STATUS_INVALID_HANDLE
;
407 * Inherit the console from the parent,
408 * if any, otherwise return an error.
410 Status
= ConSrvInheritConsole(TargetProcessData
,
411 SourceProcessData
->ConsoleHandle
,
413 &AttachConsoleRequest
->InputHandle
,
414 &AttachConsoleRequest
->OutputHandle
,
415 &AttachConsoleRequest
->ErrorHandle
);
416 if (!NT_SUCCESS(Status
))
418 DPRINT1("Console inheritance failed\n");
422 /* Return the console handle and the input wait handle to the caller */
423 AttachConsoleRequest
->ConsoleHandle
= TargetProcessData
->ConsoleHandle
;
424 AttachConsoleRequest
->InputWaitHandle
= TargetProcessData
->ConsoleEvent
;
426 /* Set the Property-Dialog and Control-Dispatcher handlers */
427 TargetProcessData
->PropDispatcher
= AttachConsoleRequest
->PropDispatcher
;
428 TargetProcessData
->CtrlDispatcher
= AttachConsoleRequest
->CtrlDispatcher
;
430 Status
= STATUS_SUCCESS
;
433 /* Unlock the "source" process and exit */
434 CsrUnlockProcess(SourceProcess
);
438 CSR_API(SrvFreeConsole
)
440 ConSrvRemoveConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
));
441 return STATUS_SUCCESS
;
445 ConDrvGetConsoleMode(IN PCONSOLE Console
,
446 IN PCONSOLE_IO_OBJECT Object
,
447 OUT PULONG ConsoleMode
);
448 CSR_API(SrvGetConsoleMode
)
451 PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.ConsoleModeRequest
;
452 PCONSOLE_IO_OBJECT Object
;
454 Status
= ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process
),
455 ConsoleModeRequest
->ConsoleHandle
,
456 &Object
, NULL
, GENERIC_READ
, TRUE
, 0);
457 if (!NT_SUCCESS(Status
)) return Status
;
459 Status
= ConDrvGetConsoleMode(Object
->Console
, Object
,
460 &ConsoleModeRequest
->ConsoleMode
);
462 ConSrvReleaseObject(Object
, TRUE
);
467 ConDrvSetConsoleMode(IN PCONSOLE Console
,
468 IN PCONSOLE_IO_OBJECT Object
,
469 IN ULONG ConsoleMode
);
470 CSR_API(SrvSetConsoleMode
)
473 PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.ConsoleModeRequest
;
474 PCONSOLE_IO_OBJECT Object
;
476 Status
= ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process
),
477 ConsoleModeRequest
->ConsoleHandle
,
478 &Object
, NULL
, GENERIC_WRITE
, TRUE
, 0);
479 if (!NT_SUCCESS(Status
)) return Status
;
481 Status
= ConDrvSetConsoleMode(Object
->Console
, Object
,
482 ConsoleModeRequest
->ConsoleMode
);
484 ConSrvReleaseObject(Object
, TRUE
);
489 ConDrvGetConsoleTitle(IN PCONSOLE Console
,
491 IN OUT PULONG BufLength
);
492 CSR_API(SrvGetConsoleTitle
)
495 PCONSOLE_GETSETCONSOLETITLE TitleRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.TitleRequest
;
498 if (!CsrValidateMessageBuffer(ApiMessage
,
499 (PVOID
)&TitleRequest
->Title
,
500 TitleRequest
->Length
,
503 return STATUS_INVALID_PARAMETER
;
506 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), &Console
, TRUE
);
507 if (!NT_SUCCESS(Status
))
509 DPRINT1("Can't get console\n");
513 Status
= ConDrvGetConsoleTitle(Console
,
515 &TitleRequest
->Length
);
517 ConSrvReleaseConsole(Console
, TRUE
);
522 ConDrvSetConsoleTitle(IN PCONSOLE Console
,
525 CSR_API(SrvSetConsoleTitle
)
528 PCONSOLE_GETSETCONSOLETITLE TitleRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.TitleRequest
;
531 if (!CsrValidateMessageBuffer(ApiMessage
,
532 (PVOID
)&TitleRequest
->Title
,
533 TitleRequest
->Length
,
536 return STATUS_INVALID_PARAMETER
;
539 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), &Console
, TRUE
);
540 if (!NT_SUCCESS(Status
))
542 DPRINT1("Can't get console\n");
546 Status
= ConDrvSetConsoleTitle(Console
,
548 TitleRequest
->Length
);
550 if (NT_SUCCESS(Status
)) ConioChangeTitle(Console
);
552 ConSrvReleaseConsole(Console
, TRUE
);
557 ConDrvGetConsoleCP(IN PCONSOLE Console
,
560 CSR_API(SrvGetConsoleCP
)
563 PCONSOLE_GETSETINPUTOUTPUTCP ConsoleCPRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.ConsoleCPRequest
;
566 DPRINT("SrvGetConsoleCP, getting %s Code Page\n",
567 ConsoleCPRequest
->InputCP
? "Input" : "Output");
569 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), &Console
, TRUE
);
570 if (!NT_SUCCESS(Status
)) return Status
;
572 Status
= ConDrvGetConsoleCP(Console
,
573 &ConsoleCPRequest
->CodePage
,
574 ConsoleCPRequest
->InputCP
);
576 ConSrvReleaseConsole(Console
, TRUE
);
581 ConDrvSetConsoleCP(IN PCONSOLE Console
,
584 CSR_API(SrvSetConsoleCP
)
586 NTSTATUS Status
= STATUS_INVALID_PARAMETER
;
587 PCONSOLE_GETSETINPUTOUTPUTCP ConsoleCPRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.ConsoleCPRequest
;
590 DPRINT("SrvSetConsoleCP, setting %s Code Page\n",
591 ConsoleCPRequest
->InputCP
? "Input" : "Output");
593 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), &Console
, TRUE
);
594 if (!NT_SUCCESS(Status
)) return Status
;
596 Status
= ConDrvSetConsoleCP(Console
,
597 ConsoleCPRequest
->CodePage
,
598 ConsoleCPRequest
->InputCP
);
600 ConSrvReleaseConsole(Console
, TRUE
);
605 ConDrvGetConsoleProcessList(IN PCONSOLE Console
,
606 IN OUT PULONG ProcessIdsList
,
607 IN ULONG MaxIdListItems
,
608 OUT PULONG ProcessIdsTotal
);
609 CSR_API(SrvGetConsoleProcessList
)
612 PCONSOLE_GETPROCESSLIST GetProcessListRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.GetProcessListRequest
;
615 if (!CsrValidateMessageBuffer(ApiMessage
,
616 (PVOID
)&GetProcessListRequest
->pProcessIds
,
617 GetProcessListRequest
->nMaxIds
,
620 return STATUS_INVALID_PARAMETER
;
623 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), &Console
, TRUE
);
624 if (!NT_SUCCESS(Status
)) return Status
;
626 Status
= ConDrvGetConsoleProcessList(Console
,
627 GetProcessListRequest
->pProcessIds
,
628 GetProcessListRequest
->nMaxIds
,
629 &GetProcessListRequest
->nProcessIdsTotal
);
631 ConSrvReleaseConsole(Console
, TRUE
);
635 CSR_API(SrvGenerateConsoleCtrlEvent
)
638 PCONSOLE_GENERATECTRLEVENT GenerateCtrlEventRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.GenerateCtrlEventRequest
;
641 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), &Console
, TRUE
);
642 if (!NT_SUCCESS(Status
)) return Status
;
644 Status
= ConDrvConsoleProcessCtrlEvent(Console
,
645 GenerateCtrlEventRequest
->ProcessGroup
,
646 GenerateCtrlEventRequest
->Event
);
648 ConSrvReleaseConsole(Console
, TRUE
);