* Sync up to trunk head (r65074).
[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 VOID NTAPI
80 ConDrvPause(PCONSOLE Console)
81 {
82 if (!Console->UnpauseEvent)
83 Console->UnpauseEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
84 }
85
86 VOID NTAPI
87 ConDrvUnpause(PCONSOLE Console)
88 {
89 if (Console->UnpauseEvent)
90 {
91 SetEvent(Console->UnpauseEvent);
92 CloseHandle(Console->UnpauseEvent);
93 Console->UnpauseEvent = NULL;
94 }
95 }
96
97
98 /*
99 * Console accessibility check helpers
100 */
101
102 BOOLEAN NTAPI
103 ConDrvValidateConsoleState(IN PCONSOLE Console,
104 IN CONSOLE_STATE ExpectedState)
105 {
106 // if (!Console) return FALSE;
107
108 /* The console must be locked */
109 // ASSERT(Console_locked);
110
111 return (Console->State == ExpectedState);
112 }
113
114 BOOLEAN NTAPI
115 ConDrvValidateConsoleUnsafe(IN PCONSOLE Console,
116 IN CONSOLE_STATE ExpectedState,
117 IN BOOLEAN LockConsole)
118 {
119 if (!Console) return FALSE;
120
121 /*
122 * Lock the console to forbid possible console's state changes
123 * (which must be done when the console is already locked).
124 * If we don't want to lock it, it's because the lock is already
125 * held. So there must be no problems.
126 */
127 if (LockConsole) EnterCriticalSection(&Console->Lock);
128
129 // ASSERT(Console_locked);
130
131 /* Check whether the console's state is what we expect */
132 if (!ConDrvValidateConsoleState(Console, ExpectedState))
133 {
134 if (LockConsole) LeaveCriticalSection(&Console->Lock);
135 return FALSE;
136 }
137
138 return TRUE;
139 }
140
141
142 /* CONSOLE INITIALIZATION FUNCTIONS *******************************************/
143
144 VOID NTAPI
145 ConDrvInitConsoleSupport(VOID)
146 {
147 DPRINT("CONSRV: ConDrvInitConsoleSupport()\n");
148
149 /* Initialize the console list and its lock */
150 InitializeListHead(&ConsoleList);
151 RtlInitializeResource(&ListLock);
152
153 /* Should call LoadKeyboardLayout */
154 }
155
156 /* For resetting the terminal - defined in dummyterm.c */
157 VOID ResetTerminal(IN PCONSOLE Console);
158
159 NTSTATUS NTAPI
160 ConDrvInitConsole(OUT PCONSOLE* NewConsole,
161 IN PCONSOLE_INFO ConsoleInfo)
162 {
163 NTSTATUS Status;
164 // CONSOLE_INFO CapturedConsoleInfo;
165 TEXTMODE_BUFFER_INFO ScreenBufferInfo;
166 PCONSOLE Console;
167 PCONSOLE_SCREEN_BUFFER NewBuffer;
168
169 if (NewConsole == NULL || ConsoleInfo == NULL)
170 return STATUS_INVALID_PARAMETER;
171
172 *NewConsole = NULL;
173
174 /*
175 * Allocate a new console
176 */
177 Console = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(*Console));
178 if (NULL == Console)
179 {
180 DPRINT1("Not enough memory for console creation.\n");
181 return STATUS_NO_MEMORY;
182 }
183
184 /*
185 * Fix the screen buffer size if needed. The rule is:
186 * ScreenBufferSize >= ConsoleSize
187 */
188 if (ConsoleInfo->ScreenBufferSize.X < ConsoleInfo->ConsoleSize.X)
189 ConsoleInfo->ScreenBufferSize.X = ConsoleInfo->ConsoleSize.X;
190 if (ConsoleInfo->ScreenBufferSize.Y < ConsoleInfo->ConsoleSize.Y)
191 ConsoleInfo->ScreenBufferSize.Y = ConsoleInfo->ConsoleSize.Y;
192
193 /*
194 * Initialize the console
195 */
196 Console->State = CONSOLE_INITIALIZING;
197 Console->ReferenceCount = 0;
198 InitializeCriticalSection(&Console->Lock);
199
200 /* Initialize the terminal interface */
201 ResetTerminal(Console);
202
203 Console->ConsoleSize = ConsoleInfo->ConsoleSize;
204 Console->FixedSize = FALSE; // Value by default; is reseted by the terminals if needed.
205
206 /* Initialize the input buffer */
207 Status = ConDrvInitInputBuffer(Console, 0 /* ConsoleInfo->InputBufferSize */);
208 if (!NT_SUCCESS(Status))
209 {
210 DPRINT1("ConDrvInitInputBuffer: failed, Status = 0x%08lx\n", Status);
211 DeleteCriticalSection(&Console->Lock);
212 ConsoleFreeHeap(Console);
213 return Status;
214 }
215
216 /* Set-up the code page */
217 Console->InputCodePage = Console->OutputCodePage = ConsoleInfo->CodePage;
218
219 /* Initialize a new text-mode screen buffer with default settings */
220 ScreenBufferInfo.ScreenBufferSize = ConsoleInfo->ScreenBufferSize;
221 ScreenBufferInfo.ScreenAttrib = ConsoleInfo->ScreenAttrib;
222 ScreenBufferInfo.PopupAttrib = ConsoleInfo->PopupAttrib;
223 ScreenBufferInfo.IsCursorVisible = TRUE;
224 ScreenBufferInfo.CursorSize = ConsoleInfo->CursorSize;
225
226 InitializeListHead(&Console->BufferList);
227 Status = ConDrvCreateScreenBuffer(&NewBuffer,
228 Console,
229 CONSOLE_TEXTMODE_BUFFER,
230 &ScreenBufferInfo);
231 if (!NT_SUCCESS(Status))
232 {
233 DPRINT1("ConDrvCreateScreenBuffer: failed, Status = 0x%08lx\n", Status);
234 ConDrvDeinitInputBuffer(Console);
235 DeleteCriticalSection(&Console->Lock);
236 ConsoleFreeHeap(Console);
237 return Status;
238 }
239 /* Make the new screen buffer active */
240 Console->ActiveBuffer = NewBuffer;
241 Console->UnpauseEvent = NULL;
242
243 DPRINT("Console initialized\n");
244
245 /* All went right, so add the console to the list */
246 Status = InsertConsole(Console);
247 if (!NT_SUCCESS(Status))
248 {
249 /* Fail */
250 ConDrvDeleteConsole(Console);
251 return Status;
252 }
253
254 /* The initialization is finished */
255 DPRINT("Change state\n");
256 Console->State = CONSOLE_RUNNING;
257
258 /* Return the newly created console to the caller and a success code too */
259 *NewConsole = Console;
260 return STATUS_SUCCESS;
261 }
262
263 NTSTATUS NTAPI
264 ConDrvRegisterTerminal(IN PCONSOLE Console,
265 IN PTERMINAL Terminal)
266 {
267 NTSTATUS Status;
268
269 if (Console == NULL || Terminal == NULL)
270 return STATUS_INVALID_PARAMETER;
271
272 /* FIXME: Lock the console before ?? */
273
274 /*
275 * Attach the terminal to the console. Use now the TermIFace of the console,
276 * and not the user-defined temporary Terminal pointer.
277 */
278 Console->TermIFace = *Terminal;
279 Console->TermIFace.Console = Console;
280
281 /* Initialize the terminal AFTER having attached it to the console */
282 DPRINT("Finish initialization of terminal\n");
283 Status = Console->TermIFace.Vtbl->InitTerminal(&Console->TermIFace, Console);
284 if (!NT_SUCCESS(Status))
285 {
286 DPRINT1("Terminal initialization failed, Status = 0x%08lx\n", Status);
287
288 /* We failed, detach the terminal from the console */
289 Terminal->Console = NULL; // For the caller
290 ResetTerminal(Console);
291
292 return Status;
293 }
294
295 /* Copy buffer contents to screen */
296 // Terminal.Draw();
297 // ConioDrawConsole(Console);
298 DPRINT("Console drawn\n");
299
300 DPRINT("Terminal initialization done\n");
301 return STATUS_SUCCESS;
302 }
303
304 NTSTATUS NTAPI
305 ConDrvDeregisterTerminal(IN PCONSOLE Console)
306 {
307 if (Console == NULL) return STATUS_INVALID_PARAMETER;
308
309 /* FIXME: Lock the console before ?? */
310
311 /* Deinitialize the terminal BEFORE detaching it from the console */
312 Console->TermIFace.Vtbl->DeinitTerminal(&Console->TermIFace/*, Console*/);
313
314 /*
315 * Detach the terminal from the console:
316 * reinitialize the terminal interface.
317 */
318 ResetTerminal(Console);
319
320 DPRINT("Terminal unregistered\n");
321 return STATUS_SUCCESS;
322 }
323
324 VOID NTAPI
325 ConDrvDeleteConsole(IN PCONSOLE Console)
326 {
327 DPRINT("ConDrvDeleteConsole(0x%p)\n", Console);
328
329 /*
330 * Forbid validation of any console by other threads
331 * during the deletion of this console.
332 */
333 ConDrvLockConsoleListExclusive();
334
335 /*
336 * If the console is already being destroyed, i.e. not running
337 * or finishing to be initialized, just return.
338 */
339 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE) &&
340 !ConDrvValidateConsoleUnsafe(Console, CONSOLE_INITIALIZING, TRUE))
341 {
342 /* Unlock the console list and return */
343 ConDrvUnlockConsoleList();
344 return;
345 }
346
347 /*
348 * We are about to be destroyed. Signal it to other people
349 * so that they can terminate what they are doing, and that
350 * they cannot longer validate the console.
351 */
352 Console->State = CONSOLE_TERMINATING;
353
354 /*
355 * Allow other threads to finish their job: basically, unlock
356 * all other calls to EnterCriticalSection(&Console->Lock); by
357 * ConDrvValidateConsoleUnsafe functions so that they just see
358 * that we are not in CONSOLE_RUNNING state anymore, or unlock
359 * other concurrent calls to ConDrvDeleteConsole so that they
360 * can see that we are in fact already deleting the console.
361 */
362 LeaveCriticalSection(&Console->Lock);
363 ConDrvUnlockConsoleList();
364
365 /* FIXME: Send a terminate message to all the processes owning this console */
366
367 /* Deregister the terminal */
368 DPRINT("Deregister terminal\n");
369 ConDrvDeregisterTerminal(Console);
370 DPRINT("Terminal deregistered\n");
371
372 /***
373 * Check that the console is in terminating state before continuing
374 * (the cleanup code must not change the state of the console...
375 * ...unless to cancel console deletion ?).
376 ***/
377
378 ConDrvLockConsoleListExclusive();
379
380 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_TERMINATING, TRUE))
381 {
382 ConDrvUnlockConsoleList();
383 return;
384 }
385
386 /* We are now in destruction */
387 Console->State = CONSOLE_IN_DESTRUCTION;
388
389 /* We really delete the console. Reset the count to be sure. */
390 Console->ReferenceCount = 0;
391
392 /* Remove the console from the list */
393 RemoveConsole(Console);
394
395 /* Delete the last screen buffer */
396 ConDrvDeleteScreenBuffer(Console->ActiveBuffer);
397 Console->ActiveBuffer = NULL;
398 if (!IsListEmpty(&Console->BufferList))
399 {
400 /***ConDrvUnlockConsoleList();***/
401 ASSERTMSG("BUGBUGBUG!! screen buffer list not empty\n", FALSE);
402 }
403
404 /* Deinitialize the input buffer */
405 ConDrvDeinitInputBuffer(Console);
406
407 if (Console->UnpauseEvent) CloseHandle(Console->UnpauseEvent);
408
409 DPRINT("ConDrvDeleteConsole - Unlocking\n");
410 LeaveCriticalSection(&Console->Lock);
411 DPRINT("ConDrvDeleteConsole - Destroying lock\n");
412 DeleteCriticalSection(&Console->Lock);
413 DPRINT("ConDrvDeleteConsole - Lock destroyed ; freeing console\n");
414
415 ConsoleFreeHeap(Console);
416 DPRINT("ConDrvDeleteConsole - Console destroyed\n");
417
418 /* Unlock the console list and return */
419 ConDrvUnlockConsoleList();
420 }
421
422
423 /* PUBLIC DRIVER APIS *********************************************************/
424
425 NTSTATUS NTAPI
426 ConDrvGetConsoleMode(IN PCONSOLE Console,
427 IN PCONSOLE_IO_OBJECT Object,
428 OUT PULONG ConsoleMode)
429 {
430 NTSTATUS Status = STATUS_SUCCESS;
431
432 if (Console == NULL || Object == NULL || ConsoleMode == NULL)
433 return STATUS_INVALID_PARAMETER;
434
435 /* Validity check */
436 ASSERT(Console == Object->Console);
437
438 /*** FIXME: */ *ConsoleMode = 0; /***/
439
440 if (INPUT_BUFFER == Object->Type)
441 {
442 PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
443 *ConsoleMode = InputBuffer->Mode;
444 }
445 else if (TEXTMODE_BUFFER == Object->Type || GRAPHICS_BUFFER == Object->Type)
446 {
447 PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
448 *ConsoleMode = Buffer->Mode;
449 }
450 else
451 {
452 Status = STATUS_INVALID_HANDLE;
453 }
454
455 return Status;
456 }
457
458 NTSTATUS NTAPI
459 ConDrvSetConsoleMode(IN PCONSOLE Console,
460 IN PCONSOLE_IO_OBJECT Object,
461 IN ULONG ConsoleMode)
462 {
463 #define CONSOLE_VALID_INPUT_MODES ( ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | \
464 ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT | \
465 ENABLE_MOUSE_INPUT )
466 #define CONSOLE_VALID_OUTPUT_MODES ( ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT )
467
468 NTSTATUS Status = STATUS_SUCCESS;
469
470 if (Console == NULL || Object == NULL)
471 return STATUS_INVALID_PARAMETER;
472
473 /* Validity check */
474 ASSERT(Console == Object->Console);
475
476 if (INPUT_BUFFER == Object->Type)
477 {
478 PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
479
480 /* Only the presence of valid mode flags is allowed */
481 if (ConsoleMode & ~CONSOLE_VALID_INPUT_MODES)
482 {
483 Status = STATUS_INVALID_PARAMETER;
484 }
485 else
486 {
487 InputBuffer->Mode = (ConsoleMode & CONSOLE_VALID_INPUT_MODES);
488 }
489 }
490 else if (TEXTMODE_BUFFER == Object->Type || GRAPHICS_BUFFER == Object->Type)
491 {
492 PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
493
494 /* Only the presence of valid mode flags is allowed */
495 if (ConsoleMode & ~CONSOLE_VALID_OUTPUT_MODES)
496 {
497 Status = STATUS_INVALID_PARAMETER;
498 }
499 else
500 {
501 Buffer->Mode = (ConsoleMode & CONSOLE_VALID_OUTPUT_MODES);
502 }
503 }
504 else
505 {
506 Status = STATUS_INVALID_HANDLE;
507 }
508
509 return Status;
510 }
511
512 NTSTATUS NTAPI
513 ConDrvGetConsoleCP(IN PCONSOLE Console,
514 OUT PUINT CodePage,
515 IN BOOLEAN OutputCP)
516 {
517 if (Console == NULL || CodePage == NULL)
518 return STATUS_INVALID_PARAMETER;
519
520 *CodePage = (OutputCP ? Console->OutputCodePage : Console->InputCodePage);
521
522 return STATUS_SUCCESS;
523 }
524
525 NTSTATUS NTAPI
526 ConDrvSetConsoleCP(IN PCONSOLE Console,
527 IN UINT CodePage,
528 IN BOOLEAN OutputCP)
529 {
530 if (Console == NULL || !IsValidCodePage(CodePage))
531 return STATUS_INVALID_PARAMETER;
532
533 if (OutputCP)
534 Console->OutputCodePage = CodePage;
535 else
536 Console->InputCodePage = CodePage;
537
538 return STATUS_SUCCESS;
539 }
540
541 /* EOF */