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 * Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) 10 */ 11 12 /* INCLUDES *******************************************************************/ 13 14 #include <consrv.h> 15 #include <coninput.h> 16 #include "../../concfg/font.h" 17 18 #define NDEBUG 19 #include <debug.h> 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 Console->IsCJK = IsCJKCodePage(Console->OutputCodePage); 224 225 /* Initialize a new text-mode screen buffer with default settings */ 226 ScreenBufferInfo.ScreenBufferSize = ConsoleInfo->ScreenBufferSize; 227 ScreenBufferInfo.ScreenAttrib = ConsoleInfo->ScreenAttrib; 228 ScreenBufferInfo.PopupAttrib = ConsoleInfo->PopupAttrib; 229 ScreenBufferInfo.IsCursorVisible = TRUE; 230 ScreenBufferInfo.CursorSize = ConsoleInfo->CursorSize; 231 232 InitializeListHead(&Console->BufferList); 233 Status = ConDrvCreateScreenBuffer(&NewBuffer, 234 Console, 235 NULL, 236 CONSOLE_TEXTMODE_BUFFER, 237 &ScreenBufferInfo); 238 if (!NT_SUCCESS(Status)) 239 { 240 DPRINT1("ConDrvCreateScreenBuffer: failed, Status = 0x%08lx\n", Status); 241 ConDrvDeinitInputBuffer(Console); 242 DeleteCriticalSection(&Console->Lock); 243 ConsoleFreeHeap(Console); 244 return Status; 245 } 246 /* Make the new screen buffer active */ 247 Console->ActiveBuffer = NewBuffer; 248 Console->UnpauseEvent = NULL; 249 250 DPRINT("Console initialized\n"); 251 252 /* All went right, so add the console to the list */ 253 Status = ConDrvInsertConsole(Console); 254 if (!NT_SUCCESS(Status)) 255 { 256 /* Fail */ 257 ConDrvDeleteConsole(Console); 258 return Status; 259 } 260 261 /* The initialization is finished */ 262 DPRINT("Change state\n"); 263 Console->State = CONSOLE_RUNNING; 264 265 /* Return the newly created console to the caller and a success code too */ 266 *NewConsole = Console; 267 return STATUS_SUCCESS; 268 } 269 270 NTSTATUS NTAPI 271 ConDrvAttachTerminal(IN PCONSOLE Console, 272 IN PTERMINAL Terminal) 273 { 274 NTSTATUS Status; 275 276 if (Console == NULL || Terminal == NULL) 277 return STATUS_INVALID_PARAMETER; 278 279 /* FIXME: Lock the console before ?? */ 280 281 /* 282 * Attach the terminal to the console. Use now the TermIFace of the console, 283 * and not the user-defined temporary Terminal pointer. 284 */ 285 Console->TermIFace = *Terminal; 286 Console->TermIFace.Console = Console; 287 288 /* Initialize the terminal AFTER having attached it to the console */ 289 DPRINT("Finish initialization of terminal\n"); 290 Status = Console->TermIFace.Vtbl->InitTerminal(&Console->TermIFace, Console); 291 if (!NT_SUCCESS(Status)) 292 { 293 DPRINT1("Terminal initialization failed, Status = 0x%08lx\n", Status); 294 295 /* We failed, detach the terminal from the console */ 296 Terminal->Console = NULL; // For the caller 297 ResetTerminal(Console); 298 return Status; 299 } 300 301 /* Copy buffer contents to screen */ 302 // Terminal.Draw(); 303 304 DPRINT("Terminal initialization done\n"); 305 return STATUS_SUCCESS; 306 } 307 308 NTSTATUS NTAPI 309 ConDrvDetachTerminal(IN PCONSOLE Console) 310 { 311 if (Console == NULL) return STATUS_INVALID_PARAMETER; 312 313 /* FIXME: Lock the console before ?? */ 314 315 /* Deinitialize the terminal BEFORE detaching it from the console */ 316 Console->TermIFace.Vtbl->DeinitTerminal(&Console->TermIFace/*, Console*/); 317 318 /* 319 * Detach the terminal from the console: 320 * reinitialize the terminal interface. 321 */ 322 ResetTerminal(Console); 323 324 DPRINT("Terminal unregistered\n"); 325 return STATUS_SUCCESS; 326 } 327 328 VOID NTAPI 329 ConDrvDeleteConsole(IN PCONSOLE Console) 330 { 331 DPRINT("ConDrvDeleteConsole(0x%p)\n", Console); 332 333 /* 334 * Forbid validation of any console by other threads 335 * during the deletion of this console. 336 */ 337 ConDrvLockConsoleListExclusive(); 338 339 /* 340 * If the console is already being destroyed, i.e. not running 341 * or finishing to be initialized, just return. 342 */ 343 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE) && 344 !ConDrvValidateConsoleUnsafe(Console, CONSOLE_INITIALIZING, TRUE)) 345 { 346 /* Unlock the console list and return */ 347 ConDrvUnlockConsoleList(); 348 return; 349 } 350 351 /* 352 * We are about to be destroyed. Signal it to other people 353 * so that they can terminate what they are doing, and that 354 * they cannot longer validate the console. 355 */ 356 Console->State = CONSOLE_TERMINATING; 357 358 /* 359 * Allow other threads to finish their job: basically, unlock 360 * all other calls to EnterCriticalSection(&Console->Lock); by 361 * ConDrvValidateConsoleUnsafe functions so that they just see 362 * that we are not in CONSOLE_RUNNING state anymore, or unlock 363 * other concurrent calls to ConDrvDeleteConsole so that they 364 * can see that we are in fact already deleting the console. 365 */ 366 LeaveCriticalSection(&Console->Lock); 367 ConDrvUnlockConsoleList(); 368 369 /* Deregister the terminal */ 370 DPRINT("Deregister terminal\n"); 371 ConDrvDetachTerminal(Console); 372 DPRINT("Terminal deregistered\n"); 373 374 /*** 375 * Check that the console is in terminating state before continuing 376 * (the cleanup code must not change the state of the console... 377 * ...unless to cancel console deletion ?). 378 ***/ 379 380 ConDrvLockConsoleListExclusive(); 381 382 if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_TERMINATING, TRUE)) 383 { 384 ConDrvUnlockConsoleList(); 385 return; 386 } 387 388 /* We are now in destruction */ 389 Console->State = CONSOLE_IN_DESTRUCTION; 390 391 /* We really delete the console. Reset the count to be sure. */ 392 Console->ReferenceCount = 0; 393 394 /* Remove the console from the list */ 395 RemoveConsole(Console); 396 397 /* Delete the last screen buffer */ 398 ConDrvDeleteScreenBuffer(Console->ActiveBuffer); 399 Console->ActiveBuffer = NULL; 400 if (!IsListEmpty(&Console->BufferList)) 401 { 402 /***ConDrvUnlockConsoleList();***/ 403 ASSERTMSG("BUGBUGBUG!! screen buffer list not empty\n", FALSE); 404 } 405 406 /* Deinitialize the input buffer */ 407 ConDrvDeinitInputBuffer(Console); 408 409 if (Console->UnpauseEvent) CloseHandle(Console->UnpauseEvent); 410 411 DPRINT("ConDrvDeleteConsole - Unlocking\n"); 412 LeaveCriticalSection(&Console->Lock); 413 DPRINT("ConDrvDeleteConsole - Destroying lock\n"); 414 DeleteCriticalSection(&Console->Lock); 415 DPRINT("ConDrvDeleteConsole - Lock destroyed ; freeing console\n"); 416 417 ConsoleFreeHeap(Console); 418 DPRINT("ConDrvDeleteConsole - Console destroyed\n"); 419 420 /* Unlock the console list and return */ 421 ConDrvUnlockConsoleList(); 422 } 423 424 425 /* PUBLIC DRIVER APIS *********************************************************/ 426 427 NTSTATUS NTAPI 428 ConDrvGetConsoleMode(IN PCONSOLE Console, 429 IN PCONSOLE_IO_OBJECT Object, 430 OUT PULONG ConsoleMode) 431 { 432 NTSTATUS Status = STATUS_SUCCESS; 433 434 if (Console == NULL || Object == NULL || ConsoleMode == NULL) 435 return STATUS_INVALID_PARAMETER; 436 437 /* Validity check */ 438 ASSERT(Console == Object->Console); 439 440 /*** FIXME: */ *ConsoleMode = 0; /***/ 441 442 if (INPUT_BUFFER == Object->Type) 443 { 444 PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object; 445 *ConsoleMode = InputBuffer->Mode; 446 } 447 else if (TEXTMODE_BUFFER == Object->Type || GRAPHICS_BUFFER == Object->Type) 448 { 449 PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object; 450 *ConsoleMode = Buffer->Mode; 451 } 452 else 453 { 454 Status = STATUS_INVALID_HANDLE; 455 } 456 457 return Status; 458 } 459 460 NTSTATUS NTAPI 461 ConDrvSetConsoleMode(IN PCONSOLE Console, 462 IN PCONSOLE_IO_OBJECT Object, 463 IN ULONG ConsoleMode) 464 { 465 #define CONSOLE_VALID_INPUT_MODES ( ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | \ 466 ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT | \ 467 ENABLE_MOUSE_INPUT ) 468 #define CONSOLE_VALID_OUTPUT_MODES ( ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT ) 469 470 NTSTATUS Status = STATUS_SUCCESS; 471 472 if (Console == NULL || Object == NULL) 473 return STATUS_INVALID_PARAMETER; 474 475 /* Validity check */ 476 ASSERT(Console == Object->Console); 477 478 if (INPUT_BUFFER == Object->Type) 479 { 480 PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object; 481 482 /* Only the presence of valid mode flags is allowed */ 483 if (ConsoleMode & ~CONSOLE_VALID_INPUT_MODES) 484 { 485 Status = STATUS_INVALID_PARAMETER; 486 } 487 else 488 { 489 InputBuffer->Mode = (ConsoleMode & CONSOLE_VALID_INPUT_MODES); 490 } 491 } 492 else if (TEXTMODE_BUFFER == Object->Type || GRAPHICS_BUFFER == Object->Type) 493 { 494 PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object; 495 496 /* Only the presence of valid mode flags is allowed */ 497 if (ConsoleMode & ~CONSOLE_VALID_OUTPUT_MODES) 498 { 499 Status = STATUS_INVALID_PARAMETER; 500 } 501 else 502 { 503 Buffer->Mode = (ConsoleMode & CONSOLE_VALID_OUTPUT_MODES); 504 } 505 } 506 else 507 { 508 Status = STATUS_INVALID_HANDLE; 509 } 510 511 return Status; 512 } 513 514 NTSTATUS NTAPI 515 ConDrvGetConsoleCP(IN PCONSOLE Console, 516 OUT PUINT CodePage, 517 IN BOOLEAN OutputCP) 518 { 519 if (Console == NULL || CodePage == NULL) 520 return STATUS_INVALID_PARAMETER; 521 522 *CodePage = (OutputCP ? Console->OutputCodePage : Console->InputCodePage); 523 524 return STATUS_SUCCESS; 525 } 526 527 NTSTATUS NTAPI 528 ConDrvSetConsoleCP(IN PCONSOLE Console, 529 IN UINT CodePage, 530 IN BOOLEAN OutputCP) 531 { 532 if (Console == NULL || !IsValidCodePage(CodePage)) 533 return STATUS_INVALID_PARAMETER; 534 535 if (OutputCP) 536 { 537 Console->OutputCodePage = CodePage; 538 Console->IsCJK = IsCJKCodePage(CodePage); 539 } 540 else 541 { 542 Console->InputCodePage = CodePage; 543 } 544 545 return STATUS_SUCCESS; 546 } 547 548 /* EOF */ 549