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 */ 542