]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Application/Shell/ConsoleLogger.c
Fix display color error when scroll up/down the screen.
[mirror_edk2.git] / ShellPkg / Application / Shell / ConsoleLogger.c
1 /** @file
2 Provides interface to shell console logger.
3
4 Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 **/
13
14 #include "ConsoleLogger.h"
15 #include "Shell.h"
16
17 STATIC CONST CHAR16 mCrLfString[3] = { CHAR_CARRIAGE_RETURN, CHAR_LINEFEED, CHAR_NULL };
18
19 /**
20 Install our intermediate ConOut into the system table to
21 keep a log of all the info that is displayed to the user.
22
23 @param[in] ScreensToSave Sets how many screen-worths of data to save.
24 @param[out] ConsoleInfo The object to pass into later functions.
25
26 @retval EFI_SUCCESS The operation was successful.
27 @return other The operation failed.
28
29 @sa ConsoleLoggerResetBuffers
30 @sa InstallProtocolInterface
31 **/
32 EFI_STATUS
33 EFIAPI
34 ConsoleLoggerInstall(
35 IN CONST UINTN ScreensToSave,
36 OUT CONSOLE_LOGGER_PRIVATE_DATA **ConsoleInfo
37 )
38 {
39 EFI_STATUS Status;
40 ASSERT(ConsoleInfo != NULL);
41
42 (*ConsoleInfo) = AllocateZeroPool(sizeof(CONSOLE_LOGGER_PRIVATE_DATA));
43 if ((*ConsoleInfo) == NULL) {
44 return (EFI_OUT_OF_RESOURCES);
45 }
46
47 (*ConsoleInfo)->Signature = CONSOLE_LOGGER_PRIVATE_DATA_SIGNATURE;
48 (*ConsoleInfo)->OldConOut = gST->ConOut;
49 (*ConsoleInfo)->OldConHandle = gST->ConsoleOutHandle;
50 (*ConsoleInfo)->Buffer = NULL;
51 (*ConsoleInfo)->BufferSize = 0;
52 (*ConsoleInfo)->OriginalStartRow = 0;
53 (*ConsoleInfo)->CurrentStartRow = 0;
54 (*ConsoleInfo)->RowsPerScreen = 0;
55 (*ConsoleInfo)->ColsPerScreen = 0;
56 (*ConsoleInfo)->Attributes = NULL;
57 (*ConsoleInfo)->AttribSize = 0;
58 (*ConsoleInfo)->ScreenCount = ScreensToSave;
59 (*ConsoleInfo)->HistoryMode.MaxMode = 1;
60 (*ConsoleInfo)->HistoryMode.Mode = 0;
61 (*ConsoleInfo)->HistoryMode.Attribute = 0;
62 (*ConsoleInfo)->HistoryMode.CursorColumn = 0;
63 (*ConsoleInfo)->HistoryMode.CursorRow = 0;
64 (*ConsoleInfo)->HistoryMode.CursorVisible = FALSE;
65 (*ConsoleInfo)->OurConOut.Reset = ConsoleLoggerReset;
66 (*ConsoleInfo)->OurConOut.OutputString = ConsoleLoggerOutputString;
67 (*ConsoleInfo)->OurConOut.TestString = ConsoleLoggerTestString;
68 (*ConsoleInfo)->OurConOut.QueryMode = ConsoleLoggerQueryMode;
69 (*ConsoleInfo)->OurConOut.SetMode = ConsoleLoggerSetMode;
70 (*ConsoleInfo)->OurConOut.SetAttribute = ConsoleLoggerSetAttribute;
71 (*ConsoleInfo)->OurConOut.ClearScreen = ConsoleLoggerClearScreen;
72 (*ConsoleInfo)->OurConOut.SetCursorPosition = ConsoleLoggerSetCursorPosition;
73 (*ConsoleInfo)->OurConOut.EnableCursor = ConsoleLoggerEnableCursor;
74 (*ConsoleInfo)->OurConOut.Mode = gST->ConOut->Mode;
75 (*ConsoleInfo)->Enabled = TRUE;
76
77 Status = ConsoleLoggerResetBuffers(*ConsoleInfo);
78 if (EFI_ERROR(Status)) {
79 SHELL_FREE_NON_NULL((*ConsoleInfo));
80 *ConsoleInfo = NULL;
81 return (Status);
82 }
83
84 Status = gBS->InstallProtocolInterface(&gImageHandle, &gEfiSimpleTextOutProtocolGuid, EFI_NATIVE_INTERFACE, (VOID*)&((*ConsoleInfo)->OurConOut));
85 if (EFI_ERROR(Status)) {
86 SHELL_FREE_NON_NULL((*ConsoleInfo)->Buffer);
87 SHELL_FREE_NON_NULL((*ConsoleInfo)->Attributes);
88 SHELL_FREE_NON_NULL((*ConsoleInfo));
89 *ConsoleInfo = NULL;
90 return (Status);
91 }
92
93 gST->ConsoleOutHandle = gImageHandle;
94 gST->ConOut = &(*ConsoleInfo)->OurConOut;
95
96 return (Status);
97 }
98
99 /**
100 Return the system to the state it was before InstallConsoleLogger
101 was installed.
102
103 @param[in] ConsoleInfo The object from the install function.
104
105 @retval EFI_SUCCESS The operation was successful
106 @return other The operation failed. This was from UninstallProtocolInterface.
107 **/
108 EFI_STATUS
109 EFIAPI
110 ConsoleLoggerUninstall(
111 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
112 )
113 {
114 ASSERT(ConsoleInfo != NULL);
115 ASSERT(ConsoleInfo->OldConOut != NULL);
116
117 if (ConsoleInfo->Buffer != NULL) {
118 FreePool(ConsoleInfo->Buffer);
119 DEBUG_CODE(ConsoleInfo->Buffer = NULL;);
120 DEBUG_CODE(ConsoleInfo->BufferSize = 0;);
121 }
122 if (ConsoleInfo->Attributes != NULL) {
123 FreePool(ConsoleInfo->Attributes);
124 DEBUG_CODE(ConsoleInfo->Attributes = NULL;);
125 DEBUG_CODE(ConsoleInfo->AttribSize = 0;);
126 }
127
128 gST->ConsoleOutHandle = ConsoleInfo->OldConHandle;
129 gST->ConOut = ConsoleInfo->OldConOut;
130
131 return (gBS->UninstallProtocolInterface(gImageHandle, &gEfiSimpleTextOutProtocolGuid, (VOID*)&ConsoleInfo->OurConOut));
132 }
133
134 /**
135 Displays previously logged output back to the screen.
136
137 This will scroll the screen forwards and backwards through the log of previous
138 output. If Rows is 0 then the size of 1/2 the screen will be scrolled. If Rows
139 is (UINTN)(-1) then the size of the screen will be scrolled.
140
141 @param[in] Forward If TRUE then the log will be displayed forwards (scroll to newer).
142 If FALSE then the log will be displayed backwards (scroll to older).
143 @param[in] Rows Determines how many rows the log should scroll.
144 @param[in] ConsoleInfo The pointer to the instance of the console logger information.
145 **/
146 EFI_STATUS
147 EFIAPI
148 ConsoleLoggerDisplayHistory(
149 IN CONST BOOLEAN Forward,
150 IN CONST UINTN Rows,
151 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
152 )
153 {
154 UINTN RowChange;
155
156 ASSERT(ConsoleInfo != NULL);
157
158 //
159 // Calculate the row number change
160 //
161 switch (Rows) {
162 case ((UINTN)(-1)):
163 RowChange = ConsoleInfo->RowsPerScreen;
164 break;
165 case (0):
166 RowChange = ConsoleInfo->RowsPerScreen / 2;
167 break;
168 default:
169 RowChange = Rows;
170 break;
171 }
172
173 //
174 // Do the math for direction
175 //
176 if (Forward) {
177 if ((ConsoleInfo->OriginalStartRow - ConsoleInfo->CurrentStartRow) < RowChange) {
178 RowChange = ConsoleInfo->OriginalStartRow - ConsoleInfo->CurrentStartRow;
179 }
180 } else {
181 if (ConsoleInfo->CurrentStartRow < RowChange) {
182 RowChange = ConsoleInfo->CurrentStartRow;
183 }
184 }
185
186 //
187 // If we are already at one end or the other
188 //
189 if (RowChange == 0) {
190 return (EFI_SUCCESS);
191 }
192
193 //
194 // Clear the screen
195 //
196 ConsoleInfo->OldConOut->ClearScreen(ConsoleInfo->OldConOut);
197
198 //
199 // Set the new start row
200 //
201 if (Forward) {
202 ConsoleInfo->CurrentStartRow += RowChange;
203 } else {
204 ConsoleInfo->CurrentStartRow -= RowChange;
205 }
206
207 //
208 // Change the screen
209 //
210 return (UpdateDisplayFromHistory(ConsoleInfo));
211 }
212
213 /**
214 Function to return to normal output whent he scrolling is complete.
215 @param[in] ConsoleInfo The pointer to the instance of the console logger information.
216
217 @retval EFI_SUCCESS The operation was successful.
218 @return other The operation failed. See UpdateDisplayFromHistory.
219
220 @sa UpdateDisplayFromHistory
221 **/
222 EFI_STATUS
223 EFIAPI
224 ConsoleLoggerStopHistory(
225 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
226 )
227 {
228 ASSERT(ConsoleInfo != NULL);
229 if (ConsoleInfo->CurrentStartRow == ConsoleInfo->OriginalStartRow) {
230 return (EFI_SUCCESS);
231 }
232 ConsoleInfo->CurrentStartRow = ConsoleInfo->OriginalStartRow;
233 return (UpdateDisplayFromHistory(ConsoleInfo));
234 }
235
236 /**
237 Updates the hidden ConOut to be displaying the correct stuff.
238 @param[in] ConsoleInfo The pointer to the instance of the console logger information.
239
240 @retval EFI_SUCCESS The operation was successful.
241 @return other The operation failed.
242 **/
243 EFI_STATUS
244 EFIAPI
245 UpdateDisplayFromHistory(
246 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
247 )
248 {
249 EFI_STATUS Status;
250 EFI_STATUS RetVal;
251 CHAR16 *Screen;
252 INT32 *Attributes;
253 UINTN CurrentRow;
254 CHAR16 TempCharHolder;
255 UINTN Column;
256 INT32 CurrentAttrib;
257 UINTN CurrentColumn;
258 CHAR16 *StringSegment;
259 CHAR16 *StringSegmentEnd;
260 CHAR16 StringSegmentEndChar;
261 INT32 OrigAttribute;
262
263 ASSERT(ConsoleInfo != NULL);
264 TempCharHolder = CHAR_NULL;
265 RetVal = EFI_SUCCESS;
266 OrigAttribute = ConsoleInfo->OldConOut->Mode->Attribute;
267
268 //
269 // Disable cursor visibility and move it to the top left corner
270 //
271 ConsoleInfo->OldConOut->EnableCursor (ConsoleInfo->OldConOut, FALSE);
272 ConsoleInfo->OldConOut->SetCursorPosition (ConsoleInfo->OldConOut, 0, 0);
273
274 Screen = &ConsoleInfo->Buffer[(ConsoleInfo->ColsPerScreen + 2) * ConsoleInfo->CurrentStartRow];
275 Attributes = &ConsoleInfo->Attributes[ConsoleInfo->ColsPerScreen * ConsoleInfo->CurrentStartRow];
276 for ( CurrentRow = 0
277 ; CurrentRow < ConsoleInfo->RowsPerScreen
278 ; CurrentRow++
279 , Screen += (ConsoleInfo->ColsPerScreen + 2)
280 , Attributes += ConsoleInfo->ColsPerScreen
281 ){
282 //
283 // dont use the last char - prevents screen scroll
284 //
285 if (CurrentRow == (ConsoleInfo->RowsPerScreen-1)){
286 TempCharHolder = Screen[ConsoleInfo->ColsPerScreen - 1];
287 Screen[ConsoleInfo->ColsPerScreen - 1] = CHAR_NULL;
288 }
289
290 for ( Column = 0
291 ; Column < ConsoleInfo->ColsPerScreen
292 ; Column++
293 ){
294 if (Screen[Column] != CHAR_NULL) {
295 CurrentAttrib = Attributes[Column];
296 CurrentColumn = Column;
297 StringSegment = &Screen[Column];
298
299 //
300 // Find the first char with a different arrribute and make that temporarily NULL
301 // so we can do fewer printout statements. (later) restore that one and we will
302 // start at that collumn on the next loop.
303 //
304 StringSegmentEndChar = CHAR_NULL;
305 for ( StringSegmentEnd = StringSegment
306 ; StringSegmentEnd != CHAR_NULL
307 ; StringSegmentEnd++
308 , Column++
309 ){
310 if (Attributes[Column] != CurrentAttrib) {
311 StringSegmentEndChar = *StringSegmentEnd;
312 *StringSegmentEnd = CHAR_NULL;
313 break;
314 }
315 } // StringSegmentEnd loop
316
317 //
318 // Now write out as much as had the same Attributes
319 //
320
321 ConsoleInfo->OldConOut->SetAttribute(ConsoleInfo->OldConOut, CurrentAttrib);
322 ConsoleInfo->OldConOut->SetCursorPosition(ConsoleInfo->OldConOut, CurrentColumn, CurrentRow);
323 Status = ConsoleInfo->OldConOut->OutputString(ConsoleInfo->OldConOut, StringSegment);
324
325 if (EFI_ERROR(Status)) {
326 ASSERT(FALSE);
327 RetVal = Status;
328 }
329
330 //
331 // If we found a change in attribute put the character back and decrement the column
332 // so when it increments it will point at that character and we will start printing
333 // a segment with that new attribute
334 //
335 if (StringSegmentEndChar != CHAR_NULL) {
336 *StringSegmentEnd = StringSegmentEndChar;
337 StringSegmentEndChar = CHAR_NULL;
338 Column--;
339 }
340 }
341 } // column for loop
342
343 //
344 // If we removed the last char and this was the last row put it back
345 //
346 if (TempCharHolder != CHAR_NULL) {
347 Screen[ConsoleInfo->ColsPerScreen - 1] = TempCharHolder;
348 TempCharHolder = CHAR_NULL;
349 }
350 } // row for loop
351
352 //
353 // If we are setting the screen back to original turn on the cursor and make it visible
354 // and set the attributes back to what they were
355 //
356 if (ConsoleInfo->CurrentStartRow == ConsoleInfo->OriginalStartRow) {
357 ConsoleInfo->OldConOut->SetAttribute (
358 ConsoleInfo->OldConOut,
359 ConsoleInfo->HistoryMode.Attribute
360 );
361 ConsoleInfo->OldConOut->SetCursorPosition (
362 ConsoleInfo->OldConOut,
363 ConsoleInfo->HistoryMode.CursorColumn,
364 ConsoleInfo->HistoryMode.CursorRow - ConsoleInfo->OriginalStartRow
365 );
366
367 Status = ConsoleInfo->OldConOut->EnableCursor (
368 ConsoleInfo->OldConOut,
369 ConsoleInfo->HistoryMode.CursorVisible
370 );
371 if (EFI_ERROR (Status)) {
372 RetVal = Status;
373 }
374 } else {
375 ConsoleInfo->OldConOut->SetAttribute (
376 ConsoleInfo->OldConOut,
377 OrigAttribute
378 );
379 }
380
381 return (RetVal);
382 }
383
384 /**
385 Reset the text output device hardware and optionaly run diagnostics
386
387 @param This pointer to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
388 @param ExtendedVerification Indicates that a more extensive test may be performed
389
390 @retval EFI_SUCCESS The text output device was reset.
391 @retval EFI_DEVICE_ERROR The text output device is not functioning correctly and
392 could not be reset.
393 **/
394 EFI_STATUS
395 EFIAPI
396 ConsoleLoggerReset (
397 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
398 IN BOOLEAN ExtendedVerification
399 )
400 {
401 EFI_STATUS Status;
402 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
403 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
404
405 //
406 // Forward the request to the original ConOut
407 //
408 Status = ConsoleInfo->OldConOut->Reset (ConsoleInfo->OldConOut, ExtendedVerification);
409
410 //
411 // Check that the buffers are still correct for logging
412 //
413 if (!EFI_ERROR (Status)) {
414 ConsoleLoggerResetBuffers(ConsoleInfo);
415 }
416
417 return Status;
418 }
419
420 /**
421 Appends a string to the history buffer. If the buffer is full then the oldest
422 information in the buffer will be dropped. Information is added in a line by
423 line manner such that an empty line takes up just as much space as a full line.
424
425 @param[in] String String pointer to add.
426 @param[in] ConsoleInfo The pointer to the instance of the console logger information.
427 **/
428 EFI_STATUS
429 EFIAPI
430 AppendStringToHistory(
431 IN CONST CHAR16 *String,
432 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
433 )
434 {
435 CONST CHAR16 *Walker;
436 UINTN CopySize;
437 UINTN PrintIndex;
438 UINTN Index;
439
440 ASSERT(ConsoleInfo != NULL);
441
442 for ( Walker = String
443 ; Walker != NULL && *Walker != CHAR_NULL
444 ; Walker++
445 ){
446 switch (*Walker) {
447 case (CHAR_BACKSPACE):
448 if (ConsoleInfo->HistoryMode.CursorColumn > 0) {
449 ConsoleInfo->HistoryMode.CursorColumn--;
450 }
451 break;
452 case (CHAR_LINEFEED):
453 if (ConsoleInfo->HistoryMode.CursorRow >= (INT32)((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount)-1)) {
454 //
455 // Should never be bigger
456 //
457 ASSERT(ConsoleInfo->HistoryMode.CursorRow == (INT32)((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount)-1));
458
459 //
460 // scroll history attributes 'up' 1 row and set the last row to default attribute
461 //
462 CopySize = ConsoleInfo->ColsPerScreen
463 * ((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount) - 1)
464 * sizeof(ConsoleInfo->Attributes[0]);
465 ASSERT(CopySize < ConsoleInfo->AttribSize);
466 CopyMem(
467 ConsoleInfo->Attributes,
468 ConsoleInfo->Attributes + ConsoleInfo->ColsPerScreen,
469 CopySize
470 );
471
472 for ( Index = 0
473 ; Index < ConsoleInfo->ColsPerScreen
474 ; Index++
475 ){
476 *(ConsoleInfo->Attributes + (CopySize/sizeof(ConsoleInfo->Attributes[0])) + Index) = ConsoleInfo->HistoryMode.Attribute;
477 }
478
479 //
480 // scroll history buffer 'up' 1 row and set the last row to spaces (L' ')
481 //
482 CopySize = (ConsoleInfo->ColsPerScreen + 2)
483 * ((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount) - 1)
484 * sizeof(ConsoleInfo->Buffer[0]);
485 ASSERT(CopySize < ConsoleInfo->BufferSize);
486 CopyMem(
487 ConsoleInfo->Buffer,
488 ConsoleInfo->Buffer + (ConsoleInfo->ColsPerScreen + 2),
489 CopySize
490 );
491
492 //
493 // Set that last row of chars to spaces
494 //
495 SetMem16(((UINT8*)ConsoleInfo->Buffer)+CopySize, ConsoleInfo->ColsPerScreen*sizeof(CHAR16), L' ');
496 } else {
497 //
498 // we are not on the last row
499 //
500
501 //
502 // We should not be scrolling history
503 //
504 ASSERT (ConsoleInfo->OriginalStartRow == ConsoleInfo->CurrentStartRow);
505 //
506 // are we at the end of a row?
507 //
508 if (ConsoleInfo->HistoryMode.CursorRow == (INT32) (ConsoleInfo->OriginalStartRow + ConsoleInfo->RowsPerScreen - 1)) {
509 ConsoleInfo->OriginalStartRow++;
510 ConsoleInfo->CurrentStartRow++;
511 }
512 ConsoleInfo->HistoryMode.CursorRow++;
513 }
514 break;
515 case (CHAR_CARRIAGE_RETURN):
516 //
517 // Move the cursor to the beginning of the current row.
518 //
519 ConsoleInfo->HistoryMode.CursorColumn = 0;
520 break;
521 default:
522 //
523 // Acrtually print characters into the history buffer
524 //
525
526 PrintIndex = ConsoleInfo->HistoryMode.CursorRow * ConsoleInfo->ColsPerScreen + ConsoleInfo->HistoryMode.CursorColumn;
527
528 for ( // no initializer needed
529 ; ConsoleInfo->HistoryMode.CursorColumn < (INT32) ConsoleInfo->ColsPerScreen
530 ; ConsoleInfo->HistoryMode.CursorColumn++
531 , PrintIndex++
532 , Walker++
533 ){
534 if (*Walker == CHAR_NULL
535 ||*Walker == CHAR_BACKSPACE
536 ||*Walker == CHAR_LINEFEED
537 ||*Walker == CHAR_CARRIAGE_RETURN
538 ){
539 Walker--;
540 break;
541 }
542 //
543 // The buffer is 2*CursorRow more since it has that many \r\n characters at the end of each row.
544 //
545
546 ASSERT(PrintIndex + ConsoleInfo->HistoryMode.CursorRow < ConsoleInfo->BufferSize);
547 ConsoleInfo->Buffer[PrintIndex + (2*ConsoleInfo->HistoryMode.CursorRow)] = *Walker;
548 ASSERT(PrintIndex < ConsoleInfo->AttribSize);
549 ConsoleInfo->Attributes[PrintIndex] = ConsoleInfo->HistoryMode.Attribute;
550 } // for loop
551
552 //
553 // Add the carriage return and line feed at the end of the lines
554 //
555 if (ConsoleInfo->HistoryMode.CursorColumn >= (INT32)ConsoleInfo->ColsPerScreen) {
556 AppendStringToHistory(L"\r\n", ConsoleInfo);
557 Walker--;
558 }
559
560 break;
561 } // switch for character
562 } // for loop
563
564 return (EFI_SUCCESS);
565 }
566
567 /**
568 Worker function to handle printing the output to the screen
569 and the history buffer
570
571 @param[in] String The string to output
572 @param[in] ConsoleInfo The pointer to the instance of the console logger information.
573
574 @retval EFI_SUCCESS The string was printed
575 @retval EFI_DEVICE_ERROR The device reported an error while attempting to output
576 the text.
577 @retval EFI_UNSUPPORTED The output device's mode is not currently in a
578 defined text mode.
579 @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the
580 characters in the Unicode string could not be
581 rendered and were skipped.
582 **/
583 EFI_STATUS
584 EFIAPI
585 ConsoleLoggerOutputStringSplit(
586 IN CONST CHAR16 *String,
587 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
588 )
589 {
590 EFI_STATUS Status;
591
592 //
593 // Forward the request to the original ConOut
594 //
595 Status = ConsoleInfo->OldConOut->OutputString (ConsoleInfo->OldConOut, (CHAR16*)String);
596
597 if (EFI_ERROR(Status)) {
598 return (Status);
599 }
600
601 return (AppendStringToHistory(String, ConsoleInfo));
602 }
603
604 /**
605 Function to handle page break mode.
606
607 This function will prompt for continue or break.
608
609 @retval EFI_SUCCESS Continue was choosen
610 @return other Break was choosen
611 **/
612 EFI_STATUS
613 EFIAPI
614 ConsoleLoggerDoPageBreak(
615 VOID
616 )
617 {
618 SHELL_PROMPT_RESPONSE *Resp;
619 EFI_STATUS Status;
620
621 Resp = NULL;
622 ASSERT(ShellInfoObject.PageBreakEnabled);
623 ShellInfoObject.PageBreakEnabled = FALSE;
624 Status = ShellPromptForResponseHii(ShellPromptResponseTypeQuitContinue, STRING_TOKEN(STR_SHELL_QUIT_CONT), ShellInfoObject.HiiHandle, (VOID**)&Resp);
625 ShellInfoObject.PageBreakEnabled = TRUE;
626 ASSERT(Resp != NULL);
627 if (Resp == NULL) {
628 return (EFI_NOT_FOUND);
629 }
630 if (EFI_ERROR(Status)) {
631 if (Resp != NULL) {
632 FreePool(Resp);
633 }
634 return (Status);
635 }
636 if (*Resp == ShellPromptResponseContinue) {
637 FreePool(Resp);
638 ShellInfoObject.ConsoleInfo->RowCounter = 0;
639 // ShellInfoObject.ConsoleInfo->OurConOut.Mode->CursorRow = 0;
640 // ShellInfoObject.ConsoleInfo->OurConOut.Mode->CursorColumn = 0;
641
642 return (EFI_SUCCESS);
643 } else if (*Resp == ShellPromptResponseQuit) {
644 FreePool(Resp);
645 ShellInfoObject.ConsoleInfo->Enabled = FALSE;
646 return (EFI_DEVICE_ERROR);
647 } else {
648 ASSERT(FALSE);
649 }
650 return (EFI_SUCCESS);
651 }
652 /**
653 Worker function to handle printing the output with page breaks.
654
655 @param[in] String The string to output
656 @param[in] ConsoleInfo The pointer to the instance of the console logger information.
657
658 @retval EFI_SUCCESS The string was printed
659 @retval EFI_DEVICE_ERROR The device reported an error while attempting to output
660 the text.
661 @retval EFI_UNSUPPORTED The output device's mode is not currently in a
662 defined text mode.
663 @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the
664 characters in the Unicode string could not be
665 rendered and were skipped.
666 **/
667 EFI_STATUS
668 EFIAPI
669 ConsoleLoggerPrintWithPageBreak(
670 IN CONST CHAR16 *String,
671 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
672 )
673 {
674 CONST CHAR16 *Walker;
675 CONST CHAR16 *LineStart;
676 CHAR16 *StringCopy;
677 CHAR16 TempChar;
678
679 StringCopy = NULL;
680 StringCopy = StrnCatGrow(&StringCopy, NULL, String, 0);
681 if (StringCopy == NULL) {
682 return (EFI_OUT_OF_RESOURCES);
683 }
684
685 for ( Walker = StringCopy
686 , LineStart = StringCopy
687 ; Walker != NULL && *Walker != CHAR_NULL
688 ; Walker++
689 ){
690 switch (*Walker) {
691 case (CHAR_BACKSPACE):
692 if (ConsoleInfo->OurConOut.Mode->CursorColumn > 0) {
693 ConsoleInfo->OurConOut.Mode->CursorColumn--;
694 }
695 break;
696 case (CHAR_LINEFEED):
697 //
698 // add a temp NULL terminator
699 //
700 TempChar = *(Walker + 1);
701 *((CHAR16*)(Walker+1)) = CHAR_NULL;
702
703 //
704 // output the string
705 //
706 ConsoleLoggerOutputStringSplit (LineStart, ConsoleInfo);
707
708 //
709 // restore the temp NULL terminator to it's original character
710 //
711 *((CHAR16*)(Walker+1)) = TempChar;
712
713 //
714 // Update LineStart Variable
715 //
716 LineStart = Walker + 1;
717
718 //
719 // increment row count
720 //
721 ShellInfoObject.ConsoleInfo->RowCounter++;
722 ConsoleInfo->OurConOut.Mode->CursorRow++;
723
724 break;
725 case (CHAR_CARRIAGE_RETURN):
726 //
727 // Move the cursor to the beginning of the current row.
728 //
729 ConsoleInfo->OurConOut.Mode->CursorColumn = 0;
730 break;
731 default:
732 //
733 // increment column count
734 //
735 ConsoleInfo->OurConOut.Mode->CursorColumn++;
736 //
737 // check if that is the last column
738 //
739 if ((INTN)ConsoleInfo->ColsPerScreen == ConsoleInfo->OurConOut.Mode->CursorColumn + 1) {
740 //
741 // output a line similar to the linefeed character.
742 //
743
744 //
745 // add a temp NULL terminator
746 //
747 TempChar = *(Walker + 1);
748 *((CHAR16*)(Walker+1)) = CHAR_NULL;
749
750 //
751 // output the string
752 //
753 ConsoleLoggerOutputStringSplit (LineStart, ConsoleInfo);
754
755 //
756 // restore the temp NULL terminator to it's original character
757 //
758 *((CHAR16*)(Walker+1)) = TempChar;
759
760 //
761 // Update LineStart Variable
762 //
763 LineStart = Walker + 1;
764
765 //
766 // increment row count and zero the column
767 //
768 ShellInfoObject.ConsoleInfo->RowCounter++;
769 ConsoleInfo->OurConOut.Mode->CursorRow++;
770 ConsoleInfo->OurConOut.Mode->CursorColumn = 0;
771 } // last column on line
772 break;
773 } // switch for character
774
775 //
776 // check if that was the last printable row. If yes handle PageBreak mode
777 //
778 if ((ConsoleInfo->RowsPerScreen) -1 == ShellInfoObject.ConsoleInfo->RowCounter) {
779 if (EFI_ERROR(ConsoleLoggerDoPageBreak())) {
780 //
781 // We got an error which means 'break' and halt the printing
782 //
783 SHELL_FREE_NON_NULL(StringCopy);
784 return (EFI_DEVICE_ERROR);
785 }
786 }
787 } // for loop
788
789 if (LineStart != NULL && *LineStart != CHAR_NULL) {
790 ConsoleLoggerOutputStringSplit (LineStart, ConsoleInfo);
791 }
792
793 SHELL_FREE_NON_NULL(StringCopy);
794 return (EFI_SUCCESS);
795 }
796
797 /**
798 Write a Unicode string to the output device.
799
800 @param[in] This Protocol instance pointer.
801 @param[in] WString The NULL-terminated Unicode string to be displayed on the output
802 device(s). All output devices must also support the Unicode
803 drawing defined in this file.
804 @retval EFI_SUCCESS The string was output to the device.
805 @retval EFI_DEVICE_ERROR The device reported an error while attempting to output
806 the text.
807 @retval EFI_UNSUPPORTED The output device's mode is not currently in a
808 defined text mode.
809 @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the
810 characters in the Unicode string could not be
811 rendered and were skipped.
812 **/
813 EFI_STATUS
814 EFIAPI
815 ConsoleLoggerOutputString (
816 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
817 IN CHAR16 *WString
818 )
819 {
820 EFI_INPUT_KEY Key;
821 UINTN EventIndex;
822 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
823 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
824 if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) {
825 return (EFI_UNSUPPORTED);
826 }
827 ASSERT(ShellInfoObject.ConsoleInfo == ConsoleInfo);
828 if (ShellInfoObject.HaltOutput) {
829 //
830 // just get some key
831 //
832 gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex);
833 gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
834 ShellInfoObject.HaltOutput = FALSE;
835 }
836 if (!ShellInfoObject.ConsoleInfo->Enabled) {
837 return (EFI_DEVICE_ERROR);
838 } else if (ShellInfoObject.PageBreakEnabled) {
839 return (ConsoleLoggerPrintWithPageBreak(WString, ConsoleInfo));
840 } else {
841 return (ConsoleLoggerOutputStringSplit(WString, ConsoleInfo));
842 }
843 }
844
845 /**
846 Verifies that all characters in a Unicode string can be output to the
847 target device.
848
849 @param[in] This Protocol instance pointer.
850 @param[in] WString The NULL-terminated Unicode string to be examined for the output
851 device(s).
852
853 @retval EFI_SUCCESS The device(s) are capable of rendering the output string.
854 @retval EFI_UNSUPPORTED Some of the characters in the Unicode string cannot be
855 rendered by one or more of the output devices mapped
856 by the EFI handle.
857
858 **/
859 EFI_STATUS
860 EFIAPI
861 ConsoleLoggerTestString (
862 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
863 IN CHAR16 *WString
864 )
865 {
866 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
867 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
868 //
869 // Forward the request to the original ConOut
870 //
871 return (ConsoleInfo->OldConOut->TestString (ConsoleInfo->OldConOut, WString));
872 }
873
874 /**
875 Returns information for an available text mode that the output device(s)
876 supports.
877
878 @param[in] This Protocol instance pointer.
879 @param[in] ModeNumber The mode number to return information on.
880 @param[out] Columns Upon return, the number of columns in the selected geometry
881 @param[out] Rows Upon return, the number of rows in the selected geometry
882
883 @retval EFI_SUCCESS The requested mode information was returned.
884 @retval EFI_DEVICE_ERROR The device had an error and could not
885 complete the request.
886 @retval EFI_UNSUPPORTED The mode number was not valid.
887 **/
888 EFI_STATUS
889 EFIAPI
890 ConsoleLoggerQueryMode (
891 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
892 IN UINTN ModeNumber,
893 OUT UINTN *Columns,
894 OUT UINTN *Rows
895 )
896 {
897 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
898 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
899 //
900 // Forward the request to the original ConOut
901 //
902 return (ConsoleInfo->OldConOut->QueryMode (
903 ConsoleInfo->OldConOut,
904 ModeNumber,
905 Columns,
906 Rows
907 ));
908 }
909
910 /**
911 Sets the output device(s) to a specified mode.
912
913 @param[in] This Protocol instance pointer.
914 @param[in] ModeNumber The mode number to set.
915
916
917 @retval EFI_SUCCESS The requested text mode was set.
918 @retval EFI_DEVICE_ERROR The device had an error and
919 could not complete the request.
920 @retval EFI_UNSUPPORTED The mode number was not valid.
921 **/
922 EFI_STATUS
923 EFIAPI
924 ConsoleLoggerSetMode (
925 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
926 IN UINTN ModeNumber
927 )
928 {
929 EFI_STATUS Status;
930
931 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
932 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
933
934 //
935 // Forward the request to the original ConOut
936 //
937 Status = ConsoleInfo->OldConOut->SetMode (ConsoleInfo->OldConOut, ModeNumber);
938
939 //
940 // Check that the buffers are still correct for logging
941 //
942 if (!EFI_ERROR (Status)) {
943 ConsoleInfo->OurConOut.Mode = gST->ConOut->Mode;
944 ConsoleLoggerResetBuffers(ConsoleInfo);
945 }
946
947 return Status;
948 }
949
950 /**
951 Sets the background and foreground colors for the OutputString () and
952 ClearScreen () functions.
953
954 @param[in] This Protocol instance pointer.
955 @param[in] Attribute The attribute to set. Bits 0..3 are the foreground color, and
956 bits 4..6 are the background color. All other bits are undefined
957 and must be zero. The valid Attributes are defined in this file.
958
959 @retval EFI_SUCCESS The attribute was set.
960 @retval EFI_DEVICE_ERROR The device had an error and
961 could not complete the request.
962 @retval EFI_UNSUPPORTED The attribute requested is not defined.
963
964 **/
965 EFI_STATUS
966 EFIAPI
967 ConsoleLoggerSetAttribute (
968 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
969 IN UINTN Attribute
970 )
971 {
972 EFI_STATUS Status;
973
974 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
975 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
976
977 //
978 // Forward the request to the original ConOut
979 //
980 Status = ConsoleInfo->OldConOut->SetAttribute (ConsoleInfo->OldConOut, Attribute);
981
982 //
983 // Record console output history
984 //
985 if (!EFI_ERROR (Status)) {
986 ConsoleInfo->HistoryMode.Attribute = (INT32) Attribute;
987 }
988
989 return Status;
990 }
991
992 /**
993 Clears the output device(s) display to the currently selected background
994 color.
995
996 @param[in] This Protocol instance pointer.
997
998 @retval EFI_SUCCESS The operation completed successfully.
999 @retval EFI_DEVICE_ERROR The device had an error and
1000 could not complete the request.
1001 @retval EFI_UNSUPPORTED The output device is not in a valid text mode.
1002 **/
1003 EFI_STATUS
1004 EFIAPI
1005 ConsoleLoggerClearScreen (
1006 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
1007 )
1008 {
1009 EFI_STATUS Status;
1010 CHAR16 *Screen;
1011 INT32 *Attributes;
1012 UINTN Row;
1013 UINTN Column;
1014 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
1015
1016 if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) {
1017 return (EFI_UNSUPPORTED);
1018 }
1019
1020 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
1021
1022 //
1023 // Forward the request to the original ConOut
1024 //
1025 Status = ConsoleInfo->OldConOut->ClearScreen (ConsoleInfo->OldConOut);
1026
1027 //
1028 // Record console output history
1029 //
1030 if (!EFI_ERROR (Status)) {
1031 Screen = &ConsoleInfo->Buffer[(ConsoleInfo->ColsPerScreen + 2) * ConsoleInfo->CurrentStartRow];
1032 Attributes = &ConsoleInfo->Attributes[ConsoleInfo->ColsPerScreen * ConsoleInfo->CurrentStartRow];
1033 for ( Row = ConsoleInfo->OriginalStartRow
1034 ; Row < (ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount)
1035 ; Row++
1036 ){
1037 for ( Column = 0
1038 ; Column < ConsoleInfo->ColsPerScreen
1039 ; Column++
1040 , Screen++
1041 , Attributes++
1042 ){
1043 *Screen = L' ';
1044 *Attributes = ConsoleInfo->OldConOut->Mode->Attribute;
1045 }
1046 //
1047 // Skip the NULL on each column end in text buffer only
1048 //
1049 Screen += 2;
1050 }
1051 ConsoleInfo->HistoryMode.CursorColumn = 0;
1052 ConsoleInfo->HistoryMode.CursorRow = 0;
1053 }
1054
1055 return Status;
1056 }
1057
1058 /**
1059 Sets the current coordinates of the cursor position
1060
1061 @param[in] This Protocol instance pointer.
1062 @param[in] Column Column to put the cursor in. Must be between zero and Column returned from QueryMode
1063 @param[in] Row Row to put the cursor in. Must be between zero and Row returned from QueryMode
1064
1065 @retval EFI_SUCCESS The operation completed successfully.
1066 @retval EFI_DEVICE_ERROR The device had an error and
1067 could not complete the request.
1068 @retval EFI_UNSUPPORTED The output device is not in a valid text mode, or the
1069 cursor position is invalid for the current mode.
1070 **/
1071 EFI_STATUS
1072 EFIAPI
1073 ConsoleLoggerSetCursorPosition (
1074 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
1075 IN UINTN Column,
1076 IN UINTN Row
1077 )
1078 {
1079 EFI_STATUS Status;
1080 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
1081
1082 if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) {
1083 return (EFI_UNSUPPORTED);
1084 }
1085
1086 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
1087 //
1088 // Forward the request to the original ConOut
1089 //
1090 Status = ConsoleInfo->OldConOut->SetCursorPosition (
1091 ConsoleInfo->OldConOut,
1092 Column,
1093 Row
1094 );
1095
1096 //
1097 // Record console output history
1098 //
1099 if (!EFI_ERROR (Status)) {
1100 ConsoleInfo->HistoryMode.CursorColumn = (INT32)Column;
1101 ConsoleInfo->HistoryMode.CursorRow = (INT32)(ConsoleInfo->OriginalStartRow + Row);
1102 }
1103
1104 return Status;
1105 }
1106
1107 /**
1108 Makes the cursor visible or invisible
1109
1110 @param[in] This Protocol instance pointer.
1111 @param[in] Visible If TRUE, the cursor is set to be visible. If FALSE, the cursor is
1112 set to be invisible.
1113
1114 @retval EFI_SUCCESS The operation completed successfully.
1115 @retval EFI_DEVICE_ERROR The device had an error and could not complete the
1116 request, or the device does not support changing
1117 the cursor mode.
1118 @retval EFI_UNSUPPORTED The output device is not in a valid text mode.
1119 **/
1120 EFI_STATUS
1121 EFIAPI
1122 ConsoleLoggerEnableCursor (
1123 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
1124 IN BOOLEAN Visible
1125 )
1126 {
1127 EFI_STATUS Status;
1128
1129 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
1130 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
1131 //
1132 // Forward the request to the original ConOut
1133 //
1134 Status = ConsoleInfo->OldConOut->EnableCursor (ConsoleInfo->OldConOut, Visible);
1135
1136 //
1137 // Record console output history
1138 //
1139 if (!EFI_ERROR (Status)) {
1140 ConsoleInfo->HistoryMode.CursorVisible = Visible;
1141 }
1142
1143 return Status;
1144 }
1145
1146 /**
1147 Function to update and verify that the current buffers are correct.
1148
1149 @param[in] ConsoleInfo The pointer to the instance of the console logger information.
1150
1151 This will be used when a mode has changed or a reset ocurred to verify all
1152 history buffers.
1153 **/
1154 EFI_STATUS
1155 EFIAPI
1156 ConsoleLoggerResetBuffers(
1157 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
1158 )
1159 {
1160 EFI_STATUS Status;
1161
1162 if (ConsoleInfo->Buffer != NULL) {
1163 FreePool(ConsoleInfo->Buffer);
1164 ConsoleInfo->Buffer = NULL;
1165 ConsoleInfo->BufferSize = 0;
1166 }
1167 if (ConsoleInfo->Attributes != NULL) {
1168 FreePool(ConsoleInfo->Attributes);
1169 ConsoleInfo->Attributes = NULL;
1170 ConsoleInfo->AttribSize = 0;
1171 }
1172
1173 Status = gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &ConsoleInfo->ColsPerScreen, &ConsoleInfo->RowsPerScreen);
1174 if (EFI_ERROR(Status)){
1175 return (Status);
1176 }
1177
1178 ConsoleInfo->BufferSize = (ConsoleInfo->ColsPerScreen + 2) * ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount * sizeof(ConsoleInfo->Buffer[0]);
1179 ConsoleInfo->AttribSize = ConsoleInfo->ColsPerScreen * ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount * sizeof(ConsoleInfo->Attributes[0]);
1180
1181 ConsoleInfo->Buffer = (CHAR16*)AllocateZeroPool(ConsoleInfo->BufferSize);
1182
1183 if (ConsoleInfo->Buffer == NULL) {
1184 return (EFI_OUT_OF_RESOURCES);
1185 }
1186
1187 ConsoleInfo->Attributes = (INT32*)AllocateZeroPool(ConsoleInfo->AttribSize);
1188 if (ConsoleInfo->Attributes == NULL) {
1189 FreePool(ConsoleInfo->Buffer);
1190 ConsoleInfo->Buffer = NULL;
1191 return (EFI_OUT_OF_RESOURCES);
1192 }
1193
1194 CopyMem (&ConsoleInfo->HistoryMode, ConsoleInfo->OldConOut->Mode, sizeof (EFI_SIMPLE_TEXT_OUTPUT_MODE));
1195
1196 return (EFI_SUCCESS);
1197 }