[CONSRV] Deduplicate GetScreenBufferSizeUnits(), SmallRectToRect() and ConsoleInput...
[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 ConDrvConsoleList;
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 ConDrvInsertConsole(IN PCONSOLE Console)
41 {
42 ASSERT(Console);
43
44 /* All went right, so add the console to the list */
45 ConDrvLockConsoleListExclusive();
46
47 DPRINT("Insert in the list\n");
48 InsertTailList(&ConDrvConsoleList, &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 /* In case we already have a pause event, just exit... */
83 if (Console->UnpauseEvent) return;
84
85 /* ... otherwise create it */
86 NtCreateEvent(&Console->UnpauseEvent, EVENT_ALL_ACCESS,
87 NULL, NotificationEvent, FALSE);
88 }
89
90 VOID NTAPI
91 ConDrvUnpause(PCONSOLE Console)
92 {
93 /* In case we already freed the event, just exit... */
94 if (!Console->UnpauseEvent) return;
95
96 /* ... otherwise set and free it */
97 NtSetEvent(Console->UnpauseEvent, NULL);
98 NtClose(Console->UnpauseEvent);
99 Console->UnpauseEvent = NULL;
100 }
101
102
103 /*
104 * Console accessibility check helpers
105 */
106
107 BOOLEAN NTAPI
108 ConDrvValidateConsoleState(IN PCONSOLE Console,
109 IN CONSOLE_STATE ExpectedState)
110 {
111 // if (!Console) return FALSE;
112
113 /* The console must be locked */
114 // ASSERT(Console_locked);
115
116 return (Console->State == ExpectedState);
117 }
118
119 BOOLEAN NTAPI
120 ConDrvValidateConsoleUnsafe(IN PCONSOLE Console,
121 IN CONSOLE_STATE ExpectedState,
122 IN BOOLEAN LockConsole)
123 {
124 if (!Console) return FALSE;
125
126 /*
127 * Lock the console to forbid possible console's state changes
128 * (which must be done when the console is already locked).
129 * If we don't want to lock it, it's because the lock is already
130 * held. So there must be no problems.
131 */
132 if (LockConsole) EnterCriticalSection(&Console->Lock);
133
134 // ASSERT(Console_locked);
135
136 /* Check whether the console's state is what we expect */
137 if (!ConDrvValidateConsoleState(Console, ExpectedState))
138 {
139 if (LockConsole) LeaveCriticalSection(&Console->Lock);
140 return FALSE;
141 }
142
143 return TRUE;
144 }
145
146
147 /* CONSOLE INITIALIZATION FUNCTIONS *******************************************/
148
149 VOID NTAPI
150 ConDrvInitConsoleSupport(VOID)
151 {
152 DPRINT("CONSRV: ConDrvInitConsoleSupport()\n");
153
154 /* Initialize the console list and its lock */
155 InitializeListHead(&ConDrvConsoleList);
156 RtlInitializeResource(&ListLock);
157 }
158
159 /* For resetting the terminal - defined in dummyterm.c */
160 VOID ResetTerminal(IN PCONSOLE Console);
161
162 NTSTATUS NTAPI
163 ConDrvInitConsole(OUT PCONSOLE* NewConsole,
164 IN PCONSOLE_INFO ConsoleInfo)
165 {
166 NTSTATUS Status;
167 // CONSOLE_INFO CapturedConsoleInfo;
168 TEXTMODE_BUFFER_INFO ScreenBufferInfo;
169 PCONSOLE Console;
170 PCONSOLE_SCREEN_BUFFER NewBuffer;
171
172 if (NewConsole == NULL || ConsoleInfo == NULL)
173 return STATUS_INVALID_PARAMETER;
174
175 *NewConsole = NULL;
176
177 /*
178 * Allocate a new console
179 */
180 Console = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(*Console));
181 if (NULL == Console)
182 {
183 DPRINT1("Not enough memory for console creation.\n");
184 return STATUS_NO_MEMORY;
185 }
186
187 /*
188 * Fix the screen buffer size if needed. The rule is:
189 * ScreenBufferSize >= ConsoleSize
190 */
191 if (ConsoleInfo->ScreenBufferSize.X < ConsoleInfo->ConsoleSize.X)
192 ConsoleInfo->ScreenBufferSize.X = ConsoleInfo->ConsoleSize.X;
193 if (ConsoleInfo->ScreenBufferSize.Y < ConsoleInfo->ConsoleSize.Y)
194 ConsoleInfo->ScreenBufferSize.Y = ConsoleInfo->ConsoleSize.Y;
195
196 /*
197 * Initialize the console
198 */
199 Console->State = CONSOLE_INITIALIZING;
200 Console->ReferenceCount = 0;
201 InitializeCriticalSection(&Console->Lock);
202
203 /* Initialize the terminal interface */
204 ResetTerminal(Console);
205
206 Console->ConsoleSize = ConsoleInfo->ConsoleSize;
207 Console->FixedSize = FALSE; // Value by default; is reseted by the terminals if needed.
208
209 /* Initialize the input buffer */
210 Status = ConDrvInitInputBuffer(Console, 0 /* ConsoleInfo->InputBufferSize */);
211 if (!NT_SUCCESS(Status))
212 {
213 DPRINT1("ConDrvInitInputBuffer: failed, Status = 0x%08lx\n", Status);
214 DeleteCriticalSection(&Console->Lock);
215 ConsoleFreeHeap(Console);
216 return Status;
217 }
218
219 /* Set-up the code page */
220 if (IsValidCodePage(ConsoleInfo->CodePage))
221 Console->InputCodePage = Console->OutputCodePage = ConsoleInfo->CodePage;
222
223 /* Initialize a new text-mode screen buffer with default settings */
224 ScreenBufferInfo.ScreenBufferSize = ConsoleInfo->ScreenBufferSize;
225 ScreenBufferInfo.ScreenAttrib = ConsoleInfo->ScreenAttrib;
226 ScreenBufferInfo.PopupAttrib = ConsoleInfo->PopupAttrib;
227 ScreenBufferInfo.IsCursorVisible = TRUE;
228 ScreenBufferInfo.CursorSize = ConsoleInfo->CursorSize;
229
230 InitializeListHead(&Console->BufferList);
231 Status = ConDrvCreateScreenBuffer(&NewBuffer,
232 Console,
233 NULL,
234 CONSOLE_TEXTMODE_BUFFER,
235 &ScreenBufferInfo);
236 if (!NT_SUCCESS(Status))
237 {
238 DPRINT1("ConDrvCreateScreenBuffer: failed, Status = 0x%08lx\n", Status);
239 ConDrvDeinitInputBuffer(Console);
240 DeleteCriticalSection(&Console->Lock);
241 ConsoleFreeHeap(Console);
242 return Status;
243 }
244 /* Make the new screen buffer active */
245 Console->ActiveBuffer = NewBuffer;
246 Console->UnpauseEvent = NULL;
247
248 DPRINT("Console initialized\n");
249
250 /* All went right, so add the console to the list */
251 Status = ConDrvInsertConsole(Console);
252 if (!NT_SUCCESS(Status))
253 {
254 /* Fail */
255 ConDrvDeleteConsole(Console);
256 return Status;
257 }
258
259 /* The initialization is finished */
260 DPRINT("Change state\n");
261 Console->State = CONSOLE_RUNNING;
262
263 /* Return the newly created console to the caller and a success code too */
264 *NewConsole = Console;
265 return STATUS_SUCCESS;
266 }
267
268 NTSTATUS NTAPI
269 ConDrvAttachTerminal(IN PCONSOLE Console,
270 IN PTERMINAL Terminal)
271 {
272 NTSTATUS Status;
273
274 if (Console == NULL || Terminal == NULL)
275 return STATUS_INVALID_PARAMETER;
276
277 /* FIXME: Lock the console before ?? */
278
279 /*
280 * Attach the terminal to the console. Use now the TermIFace of the console,
281 * and not the user-defined temporary Terminal pointer.
282 */
283 Console->TermIFace = *Terminal;
284 Console->TermIFace.Console = Console;
285
286 /* Initialize the terminal AFTER having attached it to the console */
287 DPRINT("Finish initialization of terminal\n");
288 Status = Console->TermIFace.Vtbl->InitTerminal(&Console->TermIFace, Console);
289 if (!NT_SUCCESS(Status))
290 {
291 DPRINT1("Terminal initialization failed, Status = 0x%08lx\n", Status);
292
293 /* We failed, detach the terminal from the console */
294 Terminal->Console = NULL; // For the caller
295 ResetTerminal(Console);
296 return Status;
297 }
298
299 /* Copy buffer contents to screen */
300 // Terminal.Draw();
301
302 DPRINT("Terminal initialization done\n");
303 return STATUS_SUCCESS;
304 }
305
306 NTSTATUS NTAPI
307 ConDrvDetachTerminal(IN PCONSOLE Console)
308 {
309 if (Console == NULL) return STATUS_INVALID_PARAMETER;
310
311 /* FIXME: Lock the console before ?? */
312
313 /* Deinitialize the terminal BEFORE detaching it from the console */
314 Console->TermIFace.Vtbl->DeinitTerminal(&Console->TermIFace/*, Console*/);
315
316 /*
317 * Detach the terminal from the console:
318 * reinitialize the terminal interface.
319 */
320 ResetTerminal(Console);
321
322 DPRINT("Terminal unregistered\n");
323 return STATUS_SUCCESS;
324 }
325
326 VOID NTAPI
327 ConDrvDeleteConsole(IN PCONSOLE Console)
328 {
329 DPRINT("ConDrvDeleteConsole(0x%p)\n", Console);
330
331 /*
332 * Forbid validation of any console by other threads
333 * during the deletion of this console.
334 */
335 ConDrvLockConsoleListExclusive();
336
337 /*
338 * If the console is already being destroyed, i.e. not running
339 * or finishing to be initialized, just return.
340 */
341 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE) &&
342 !ConDrvValidateConsoleUnsafe(Console, CONSOLE_INITIALIZING, TRUE))
343 {
344 /* Unlock the console list and return */
345 ConDrvUnlockConsoleList();
346 return;
347 }
348
349 /*
350 * We are about to be destroyed. Signal it to other people
351 * so that they can terminate what they are doing, and that
352 * they cannot longer validate the console.
353 */
354 Console->State = CONSOLE_TERMINATING;
355
356 /*
357 * Allow other threads to finish their job: basically, unlock
358 * all other calls to EnterCriticalSection(&Console->Lock); by
359 * ConDrvValidateConsoleUnsafe functions so that they just see
360 * that we are not in CONSOLE_RUNNING state anymore, or unlock
361 * other concurrent calls to ConDrvDeleteConsole so that they
362 * can see that we are in fact already deleting the console.
363 */
364 LeaveCriticalSection(&Console->Lock);
365 ConDrvUnlockConsoleList();
366
367 /* Deregister the terminal */
368 DPRINT("Deregister terminal\n");
369 ConDrvDetachTerminal(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 */