/*++ Copyright (c) 1992 Microsoft Corporation Module Name: arcdisp.c Abstract: This module provides code for managing screen output on an ARC-compliant system. Author: John Vert (jvert) 6-Oct-1993 Revision History: John Vert (jvert) 6-Oct-1993 Taken from old 1.0 splib sources --*/ #include "setupldr.h" #include "stdio.h" // // The screen is divided into 3 areas: the header area, the status area, // and the client area. The header area basically always says "Windows NT // Setup". The status area is always 1 line high and displayed using a // different attribute (black on gray). // #define HEADER_HEIGHT 3 #define MAX_STATUS 200 // allow up to 1600 horizontal res. #define SCREEN_SIZE (ScreenWidth*ScreenHeight) ULONG ScreenWidth=80; ULONG ScreenHeight=25; ULONG ScreenX=0,ScreenY=0; UCHAR CurAttribute = DEFATT; CHAR MessageBuffer[1024]; // // private function prototypes // VOID SlpDrawMenu( IN ULONG X, IN ULONG Y, IN ULONG TopItem, IN ULONG Height, IN PSL_MENU Menu ); VOID SlpDrawMenuItem( IN ULONG X, IN ULONG Y, IN ULONG TopItem, IN ULONG Height, IN ULONG Item, IN PSL_MENU Menu ); VOID SlpSizeMessage( IN PCHAR Message, OUT PULONG Lines, OUT PULONG MaxLength, OUT ULONG LineLength[], OUT PCHAR LineText[] ); PSL_MENU SlCreateMenu( VOID ) /*++ Routine Description: Allocates and initializes a new menu structure. Arguments: None Return Value: Pointer to the new menu structure if successful. NULL on failure --*/ { PSL_MENU p; p=BlAllocateHeap(sizeof(SL_MENU)); if (p==NULL) { return(NULL); } p->ItemCount = 0; p->Width = 0; InitializeListHead(&p->ItemListHead); return(p); } BOOLEAN SlGetMenuItemIndex( IN PSL_MENU Menu, IN PCHAR Text, OUT PULONG Index ) /*++ Routine Description: Given the text of a menu item, returns the index of that item. Arguments: Menu - Supplies the menu Text - Supplies the text to search for. Index - Returns the index of the text in the menu Return Value: TRUE - Item was found. FALSE - Item was not found --*/ { ULONG i; PSL_MENUITEM Item; // // Find first item to display // Item = CONTAINING_RECORD(Menu->ItemListHead.Flink, SL_MENUITEM, ListEntry); i=0; while ( Item != CONTAINING_RECORD(&Menu->ItemListHead, SL_MENUITEM, ListEntry)) { if (_stricmp(Item->Text,Text)==0) { *Index = i; return(TRUE); } Item = CONTAINING_RECORD(Item->ListEntry.Flink, SL_MENUITEM, ListEntry); ++i; } return(FALSE); } PVOID SlGetMenuItem( IN PSL_MENU Menu, IN ULONG Item ) /*++ Routine Description: Given an item index, returns the data associated with that item. Arguments: Menu - Supplies the menu structure. Item - Supplies the item index. Return Value: The data associated with the given item. --*/ { ULONG i; PSL_MENUITEM MenuItem; // // Find item to return // MenuItem = CONTAINING_RECORD(Menu->ItemListHead.Flink, SL_MENUITEM, ListEntry); for (i=0;iListEntry.Flink, SL_MENUITEM, ListEntry); #if DBG if (&MenuItem->ListEntry == &Menu->ItemListHead) { SlError(Item); return(NULL); } #endif } return(MenuItem->Data); } ULONG SlAddMenuItem( PSL_MENU Menu, PCHAR Text, PVOID Data, ULONG Attributes ) /*++ Routine Description: Adds an item to the menu Arguments: Menu - Supplies a pointer to the menu the item will be added to Text - Supplies the text to be displayed in the menu Data - Supplies a pointer to the data to be returned when the item is selected. Attributes - Supplies any attributes for the item. Return Value: The Selection index if successful -1 on failure --*/ { PSL_MENUITEM NewItem; ULONG Length; NewItem = BlAllocateHeap(sizeof(SL_MENUITEM)); if (NewItem==NULL) { SlError(0); return((ULONG)-1); } InsertTailList(&Menu->ItemListHead, &NewItem->ListEntry); Menu->ItemCount += 1; NewItem->Text = Text; NewItem->Data = Data; NewItem->Attributes = Attributes; Length = strlen(Text); if (Length > Menu->Width) { Menu->Width = Length; } return(Menu->ItemCount - 1); } ULONG SlDisplayMenu( IN ULONG HeaderId, IN PSL_MENU Menu, IN OUT PULONG Selection ) /*++ Routine Description: Displays a menu and allows the user to pick a selection Arguments: HeaderId - Supplies the message ID of the prompt header to be displayed above the menu. Menu - Supplies a pointer to the menu to be displayed Selection - Supplies the index of the default item. Returns the index of the selected item. Return Value: Key that terminated the menu display. --*/ { LONG X, Y; ULONG Height; ULONG Width; ULONG TopItem; ULONG c; ULONG PreviousSelection; ULONG Sel; PCHAR Header; ULONG HeaderLines; ULONG MaxHeaderLength; PCHAR HeaderText[20]; ULONG HeaderLength[20]; ULONG MaxMenuHeight; ULONG i; ULONG Count; Header = BlFindMessage(HeaderId); SlpSizeMessage(Header, &HeaderLines, &MaxHeaderLength, HeaderLength, HeaderText); X = (ScreenWidth-MaxHeaderLength)/2; for (i=0;i ScreenWidth) { MaxHeaderLength = ScreenWidth; } Width = Menu->Width+4; if (Width > ScreenWidth) { Width=ScreenWidth; } MaxMenuHeight = ScreenHeight-(HeaderLines+13); Height = Menu->ItemCount+2; if (Height > MaxMenuHeight) { Height = MaxMenuHeight; } X = ((ScreenWidth - Width)/2 > 0) ? (ScreenWidth-Width)/2 : 0; Y = (MaxMenuHeight - Height)/2 + HeaderLines + 4; TopItem = 0; Sel = *Selection; // // Make sure default item is in view; // if (Sel >= Height - 2) { TopItem = Sel - Height + 3; } SlpDrawMenu(X,Y, TopItem, Height, Menu); // // highlight default selection // SlSetCurrentAttribute(INVATT); SlpDrawMenuItem(X,Y, TopItem, Height, Sel, Menu); SlSetCurrentAttribute(DEFATT); SlFlushConsoleBuffer(); do { c = SlGetChar(); PreviousSelection = Sel; SlpDrawMenuItem(X, Y, TopItem, Height, Sel, Menu); switch (c) { case SL_KEY_UP: if(Sel > 0) { Sel--; } break; case SL_KEY_DOWN: if(Sel < Menu->ItemCount - 1) { Sel++; } break; case SL_KEY_HOME: Sel = 0; break; case SL_KEY_END: Sel = Menu->ItemCount - 1; break; case SL_KEY_PAGEUP: if (Menu->ItemCount > Height) { if (Sel > Height) { Sel -= Height; } else { Sel = 0; } } break; case SL_KEY_PAGEDOWN: if (Menu->ItemCount > Height) { Sel += Height; if (Sel >= Menu->ItemCount) { Sel = Menu->ItemCount - 1; } } break; case SL_KEY_F1: case SL_KEY_F3: case ASCI_CR: case ASCI_ESC: *Selection = Sel; return(c); } if (Sel < TopItem) { TopItem = Sel; SlpDrawMenu(X, Y, TopItem, Height, Menu); } else if (Sel > TopItem+Height-3) { TopItem = Sel - Height + 3; SlpDrawMenu(X, Y, TopItem, Height, Menu); } // // highlight default selection // SlSetCurrentAttribute(INVATT); SlpDrawMenuItem(X,Y, TopItem, Height, Sel, Menu); SlSetCurrentAttribute(DEFATT); } while ( TRUE ); } VOID SlpDrawMenu( IN ULONG X, IN ULONG Y, IN ULONG TopItem, IN ULONG Height, IN PSL_MENU Menu ) /*++ Routine Description: Displays the menu on the screen Arguments: X - Supplies X coordinate of upper-left corner of menu Y - Supplies Y coordinate of upper-left corner of menu TopItem - Supplies index of item at the top of the menu Height - Supplies the height of the menu Menu - Supplies the menu to be displayed Return Value: None. --*/ { ULONG i; PSL_MENUITEM Item; ULONG Count; CHAR Output[80]; ULONG Length; ULONG MenuWidth; MenuWidth = Menu->Width+4; Output[0]=GetGraphicsChar(GraphicsCharDoubleRightDoubleDown); for (i=1;iItemListHead.Flink, SL_MENUITEM, ListEntry); for (i=0;iListEntry.Flink, SL_MENUITEM, ListEntry); } // // Display items // Output[0]= Output[MenuWidth-1]=GetGraphicsChar(GraphicsCharDoubleVertical); for (i=Y+1;iListEntry != &Menu->ItemListHead) { Length = strlen(Item->Text); RtlCopyMemory(Output+2,Item->Text,Length); Item = CONTAINING_RECORD(Item->ListEntry.Flink, SL_MENUITEM, ListEntry); } ArcWrite(ARC_CONSOLE_OUTPUT,Output,MenuWidth,&Count); } Output[0]=GetGraphicsChar(GraphicsCharDoubleRightDoubleUp); for (i=1;iItemListHead.Flink, SL_MENUITEM, ListEntry); for (i=0;iListEntry.Flink, SL_MENUITEM, ListEntry); #if DBG if (&MenuItem->ListEntry == &Menu->ItemListHead) { SlError(Item); } #endif } RtlFillMemory(Width,Menu->Width,' '); RtlCopyMemory(Width,MenuItem->Text,strlen(MenuItem->Text)); SlPositionCursor(X+2, Y+(Item-TopItem)+1); ArcWrite(ARC_CONSOLE_OUTPUT,Width,Menu->Width,&Count); } VOID SlInitDisplay( VOID ) /*++ Routine Description: Clears the screen and does some initialization of global variables based on the ARC display information. Arguments: None Return Value: None. --*/ { PARC_DISPLAY_STATUS DisplayStatus; // // Check to see if this version of the ARC firmware is revision 2 or above. // // If not, we default to 80x25 // if ((SYSTEM_BLOCK->Version > 1) || ((SYSTEM_BLOCK->Version == 1) && (SYSTEM_BLOCK->Revision >= 2))) { // // Additional checks are required on 1.2 firmware, since some // 1.2 firmware does not implement ArcGetDisplayStatus // if ((SYSTEM_BLOCK->FirmwareVectorLength > (ULONG)GetDisplayStatusRoutine*sizeof(PVOID)) && (SYSTEM_BLOCK->FirmwareVector[GetDisplayStatusRoutine] != NULL)) { DisplayStatus = ArcGetDisplayStatus(ARC_CONSOLE_OUTPUT); ScreenWidth = DisplayStatus->CursorMaxXPosition; ScreenHeight = DisplayStatus->CursorMaxYPosition; } } SlSetCurrentAttribute(DEFATT); SlClearDisplay(); } VOID SlPrint( IN PCHAR FormatString, ... ) { va_list arglist; CHAR text[MAX_STATUS+1]; ULONG Count,Length; ULONG MaxWidth = ScreenWidth - 2; if (MaxWidth > MAX_STATUS) { MaxWidth = MAX_STATUS; } va_start(arglist,FormatString); Length = _vsnprintf(text,MaxWidth*sizeof(CHAR),FormatString,arglist); text[MaxWidth] = 0; ArcWrite(ARC_CONSOLE_OUTPUT,text,Length,&Count); va_end(arglist); } VOID SlClearDisplay( VOID ) /*++ Routine Description: Clears the entire display, including header, client area, and status line. Arguments: None Return Value: None. --*/ { SlClearClientArea(); SlWriteHeaderText(0); SlWriteStatusText(""); } ARC_STATUS SlClearClientArea( VOID ) /*++ Routine Description: Clears the client area of the screen. Does not disturb the header or status areas. Arguments: None. Return Value: always ESUCCESS --*/ { USHORT i; for(i=HEADER_HEIGHT; i=ScreenWidth) { x = ScreenWidth-1; } if(y>=ScreenHeight) { y = ScreenHeight-1; } ScreenX = x; ScreenY = y; SlPrint(ASCI_CSI_OUT "%d;%dH",y+1,x+1); return(ESUCCESS); } ARC_STATUS SlWriteString( IN PUCHAR s ) { PUCHAR p = s,q; BOOLEAN done = FALSE; ULONG len,count; do { q = p; while((*q != '\0') && (*q != '\n')) { q++; } if(*q == '\0') { done = TRUE; } else { *q = '\0'; } len = q - p; ArcWrite(ARC_CONSOLE_OUTPUT,p,len,&count); ScreenX += len; if(!done) { ArcWrite(ARC_CONSOLE_OUTPUT,"\r\n",2,&count); ScreenX = 0; ScreenY++; if(ScreenY == ScreenHeight) { ScreenY = ScreenHeight-1; } *q = '\n'; } p = q + 1; } while(!done); return(ESUCCESS); } VOID SlSetCurrentAttribute( IN UCHAR Attribute ) { CurAttribute = Attribute; SlPrint(ASCI_CSI_OUT "0m" ASCI_CSI_OUT "3%dm" ASCI_CSI_OUT "4%dm", Attribute & 7, // foreground color (Attribute >> 4) & 7 // background color ); if(Attribute & ATT_FG_INTENSE) { SlPrint(ASCI_CSI_OUT "1m"); } } VOID SlWriteHeaderText( IN ULONG MsgId ) /*++ Routine Description: Updates the header on the screen with a given string Arguments: MsgId - Supplies the message ID of the new string to be displayed. This should be just one line long. If it is 0, the header is cleared. Return Value: None. --*/ { int i; for(i=HEADER_HEIGHT-1; i>=0; i--) { SlPositionCursor(0,i); SlClearToEol(); } if (MsgId != 0) { SlWriteString(BlFindMessage(MsgId)); } } // // Stores the current status text. The size is the screen width, plus the // terminating nul char. // CHAR StatusText[MAX_STATUS]; UCHAR StatusAttribute = DEFSTATTR; VOID SlSetStatusAttribute( IN UCHAR Attribute ) { StatusAttribute = Attribute; } VOID SlWriteStatusText( IN PCHAR Text ) /*++ Routine Description: Updates the status area on the screen with a given string Arguments: Text - Supplies the new text for the status area. Return Value: None. --*/ { UCHAR AttributeSave = CurAttribute; CHAR *p; ULONG Count; ULONG MaxWidth = ScreenWidth - 2; if (MaxWidth > MAX_STATUS) { MaxWidth = MAX_STATUS; } RtlFillMemory(StatusText,sizeof(StatusText),' '); // // Strip cr/lf as we copy the status text into the status text buffer. // p = StatusText; Count = 0; while((Count < MaxWidth) && *Text) { if((*Text != '\r') && (*Text != '\n')) { *p++ = *Text; Count++; } Text++; } SlSetCurrentAttribute(StatusAttribute); SlPositionCursor(0,ScreenHeight-1); ArcWrite(ARC_CONSOLE_OUTPUT," ",2,&Count); SlPositionCursor(2,ScreenHeight-1); ArcWrite(ARC_CONSOLE_OUTPUT,StatusText,MaxWidth*sizeof(CHAR),&Count); SlSetCurrentAttribute(AttributeSave); SlPositionCursor(0,5); } VOID SlGetStatusText( OUT PCHAR Text ) { RtlCopyMemory(Text,StatusText,sizeof(StatusText)); } #if DBG VOID SlWriteDbgText( IN PCHAR text ) { UCHAR SavedAttribute = CurAttribute; SlPositionCursor(0,0); CurAttribute = ATT_FG_YELLOW | ATT_BG_RED | ATT_FG_INTENSE; SlClearToEol(); SlWriteString(text); CurAttribute = SavedAttribute; } #endif VOID SlFlushConsoleBuffer( VOID ) /*++ Routine Description: This routine flushes the console buffer, so that we don't have any pre-existing keypresses in the buffer when we prompt the user to 'press any key to continue.' Arguments: NONE Return Value: NONE --*/ { UCHAR c; ULONG count; while(ArcGetReadStatus(ARC_CONSOLE_INPUT) == ESUCCESS) { ArcRead(ARC_CONSOLE_INPUT, &c, 1, &count); } } ULONG SlGetChar( VOID ) { UCHAR c; ULONG count; ArcRead(ARC_CONSOLE_INPUT,&c,1,&count); if(c == ASCI_CSI_IN) { ArcRead(ARC_CONSOLE_INPUT,&c,1,&count); switch(c) { // // see ntos\fw\mips\fwsignal.c!TranslateScanCode() for these codes. // Additional codes that might be useful someday: // left=C, right=D, insert=@, delete=P // case 'A': // up arrow return(SL_KEY_UP); case 'B': // down arrow return(SL_KEY_DOWN); case 'H': // home return(SL_KEY_HOME); case 'K': // end return(SL_KEY_END); case '?': // page up return(SL_KEY_PAGEUP); case '/': // page down return(SL_KEY_PAGEDOWN); case 'O': // function keys ArcRead(ARC_CONSOLE_INPUT,&c,1,&count); // // F1=P, F2=Q, F3=w, F4 =x, F5 =t, F6 =u // F7=q, F8=r, F9=p, F10=m, F11=A, F12=B // // Note: as of 12/15/92, f11 and f12 were commented out in the // firmware sources so are probably never returned. // switch(c) { case 'P': return(SL_KEY_F1); case 'w': return(SL_KEY_F3); case 't': return(SL_KEY_F5); case 'u': return(SL_KEY_F6); default: return(0); } default: return(0); } } else { if(c == ASCI_LF) { c = ASCI_CR; } return((ULONG)c); } } BOOLEAN SlPromptForDisk( IN PCHAR DiskName, IN BOOLEAN IsCancellable ) /*++ Routine Description: This routine prompts a user to insert a given diskette #, or to abort the Setup process. The status line will be erased. Arguments: DiskName - Supplies the name of the disk to be inserted. IsCancellable - Supplies flag indicating whether prompt may be cancelled. Return Value: TRUE - The user has pressed OK FALSE - The user has pressed CANCEL --*/ { ULONG msg; ULONG y; ULONG Key; PCHAR StatusText; PCHAR PleaseWait; ULONG i; CHAR DiskNameDisplayed[81]; BOOLEAN Repaint=TRUE; SlWriteStatusText(""); if(IsCancellable) { msg = SL_NEXT_DISK_PROMPT_CANCELLABLE; } else { msg = SL_NEXT_DISK_PROMPT; } StatusText = BlFindMessage(msg); if(StatusText == NULL) { SlError(msg); return(FALSE); } PleaseWait = BlFindMessage(SL_PLEASE_WAIT); if(PleaseWait == NULL) { SlError(SL_PLEASE_WAIT); return(FALSE); } // // Get first line of DiskName and save it in DiskNameDisplayed (limit to 80 chars) // for(i = 0; ((i < 80) && DiskName[i] && (DiskName[i] != '\r') && (DiskName[i] != '\n')); i++) { DiskNameDisplayed[i] = DiskName[i]; } DiskNameDisplayed[i] = '\0'; do { if (Repaint) { SlClearClientArea(); y = SlDisplayMessageBox(SL_MSG_INSERT_DISK); SlPositionCursor((ScreenWidth-i)/2,y+2); SlWriteString(DiskNameDisplayed); SlWriteStatusText(StatusText); } Repaint = FALSE; SlFlushConsoleBuffer(); Key = SlGetChar(); if (Key == ASCI_CR) { SlClearClientArea(); SlWriteStatusText(PleaseWait); return(TRUE); } else if (Key == SL_KEY_F3) { SlConfirmExit(); Repaint=TRUE; } else if((Key == ASCI_ESC) && IsCancellable) { SlWriteStatusText(""); SlClearClientArea(); return FALSE; } } while ( TRUE ); } VOID SlConfirmExit( VOID ) /*++ Routine Description: Routine to be called when user presses F3. Confirms that he really wants to exit by popping up a dialog. DOES NOT RETURN if user chooses to exit. Arguments: None. Return Value: None. --*/ { ULONG c; CHAR OldStatus[MAX_STATUS]; PCHAR StatusText; SlGetStatusText(OldStatus); SlClearClientArea(); SlSetCurrentAttribute(DEFDLGATT); SlDisplayMessageBox(SL_MSG_EXIT_DIALOG); SlSetCurrentAttribute(DEFATT); SlFlushConsoleBuffer(); while(1) { c = SlGetChar(); if(c == ASCI_CR) { SlWriteStatusText(OldStatus); return; } if(c == SL_KEY_F3) { StatusText = BlFindMessage(SL_REBOOT_PROMPT); SlClearClientArea(); #ifdef i386 SlDisplayMessageBox(SL_SCRN_TEXTSETUP_EXITED); #else SlDisplayMessageBox(SL_SCRN_TEXTSETUP_EXITED_ARC); #endif SlWriteStatusText(StatusText); SlFlushConsoleBuffer(); while(SlGetChar() != ASCI_CR); ArcRestart(); } } } VOID SlFriendlyError( IN ULONG uStatus, IN PCHAR pchBadFile, IN ULONG uLine, IN PCHAR pchCodeFile ) /*++ Routine Description: This is called when an error occurs. It puts up a message box, displays an informative message, and allows the user to continue. It is intended to give friendlier messages than the SlError macro, in the cases where SlError gets passed ARC error codes. The status text line will be erased. Arguments: uStatus - ARC error code pchBadFile - Name of file causing error (Must be given for handled ARC codes. Optional for unhandled codes.) uLine - Line # in source code file where error occurred (only used for unhandled codes.) pchCodeFile - Name of souce code file where error occurred (only used for unhandled codes.) Return Value: None. --*/ { ULONG uMsg; SlClearClientArea(); switch(uStatus) { case EBADF: case EINVAL: // image corrupt uMsg = SL_WARNING_IMG_CORRUPT; break; case EIO: // i/o error uMsg = SL_WARNING_IOERR; break; case ENOENT: // file not found uMsg = SL_WARNING_NOFILE; break; case ENOMEM: // insufficient memory uMsg = SL_WARNING_NOMEM; break; case EACCES: // unrecognized file system uMsg = SL_WARNING_BAD_FILESYS; break; default: // then get SlError() behavior (with optional bad file name) if(pchBadFile) { // include error-causing file's name SlMessageBox( SL_WARNING_ERROR_WFILE, pchBadFile, uStatus, uLine, pchCodeFile ); } else { SlMessageBox( SL_WARNING_ERROR, uStatus, uLine, pchCodeFile ); } return; } SlMessageBox( uMsg, pchBadFile ); } VOID SlMessageBox( IN ULONG MessageId, ... ) /*++ Routine Description: This is called when an error occurs. It puts up a message box, displays an informative message, and allows the user to continue. The status text line will be erased. Arguments: MessageId - Supplies ID of message box to be presented. any sprintf-compatible arguments to be inserted in the message box. Return Value: None. --*/ { va_list args; SlClearClientArea(); va_start(args, MessageId); SlGenericMessageBox(MessageId, &args, NULL, NULL, NULL, NULL, TRUE); va_end(args); SlFlushConsoleBuffer(); SlGetChar(); } ULONG SlDisplayMessageBox( IN ULONG MessageId, ... ) /*++ Routine Description: Just puts a message box up on the screen and returns. The status text line will be erased. Arguments: MessageId - Supplies ID of message box to be presented. any sprintf-compatible arguments to be inserted in the message box. Return Value: Y position of top line of message box --*/ { ULONG y; va_list args; va_start(args, MessageId); SlGenericMessageBox(MessageId, &args, NULL, NULL, &y, NULL, TRUE); va_end(args); return(y); } VOID SlFatalError( IN ULONG MessageId, ... ) /*++ Routine Description: This is called when a fatal error occurs. It clears the client area, puts up a message box, displays the fatal error message, and allows the user to press a key to reboot. The status text line will be erased. Arguments: MessageId - Supplies ID of message box to be presented. any sprintf-compatible arguments to be inserted in the message box. Return Value: Does not return. --*/ { va_list args; ULONG x,y; PUCHAR Text; SlClearClientArea(); Text = BlFindMessage(MessageId); if(Text) { va_start(args, MessageId); _vsnprintf(MessageBuffer, sizeof(MessageBuffer), Text, args); // // Add a blank line, then concatenate the 'Can't continue' text. // strcat(MessageBuffer, "\r\n"); Text = BlFindMessage(SL_CANT_CONTINUE); if(Text) { strcat(MessageBuffer, Text); } Text = BlAllocateHeap((strlen(MessageBuffer)+1) * sizeof(CHAR)); strcpy(Text, MessageBuffer); // // Note that MessageId and args won't be used, since we're // passing in our Text pointer. // SlGenericMessageBox(MessageId, &args, Text, &x, NULL, &y, TRUE); va_end(args); } else { SlError(MessageId); } SlFlushConsoleBuffer(); SlGetChar(); ArcRestart(); } VOID SlGenericMessageBox( IN ULONG MessageId, OPTIONAL IN va_list *args, OPTIONAL IN PCHAR Message, OPTIONAL IN OUT PULONG xLeft, OPTIONAL IN OUT PULONG yTop, OPTIONAL OUT PULONG yBottom, OPTIONAL IN BOOLEAN bCenterMsg ) /*++ Routine Description: Formats and displays a message box. The longest line in the string of characters will be centered on the screen if bCenterMsg is TRUE. The status text line will be erased. Arguments: NOTE: Either the MessageId/args pair or the Message string must be specified. Message string will be used if non-NULL. MessageId - Supplies the MessageId that will be looked up to provide a NULL-terminated string of characters. Each \r\n delimited string will be displayed on its own line. args - Supplies the argument list that will be passed to vsprintf. Message - Supplies the actual text of the message to be displayed xLeft - If bCenterMsg is FALSE, then xLeft is used for the starting x coordinate of the message (if specified, otherwise, x = 1). Also, if specified, it receives the x coordinate of the left edge of all lines that were displayed. yTop - If bCenterMsg is FALSE, then yTop is used for the starting y coordinate of the message (if specified, otherwise, y = 3). Also, if specified, receives the y coordinate of the top line where the message box was displayed. yBottom - if specified, receives the y coordinate of the bottom line of the message box. bCenterMsg - if TRUE, center message on the screen. Return Value: None. --*/ { PCHAR p; ULONG NumLines; ULONG MaxLength; ULONG x; ULONG y; ULONG i; PCHAR Line[20]; ULONG LineLength[20]; ULONG Count; if(!Message) { // then look up the message p=BlFindMessage(MessageId); if (p==NULL) { SlError(MessageId); x=3; y=ScreenHeight/2; NumLines=0; } else { _vsnprintf(MessageBuffer,sizeof(MessageBuffer),p,*args); Message = MessageBuffer; } } else { // // Just make p non-NULL, so we'll know it's OK to continue. // p = Message; } if(p) { SlWriteStatusText(""); // Clear status bar SlpSizeMessage(Message, &NumLines, &MaxLength, LineLength, Line); if (MaxLength > ScreenWidth) { MaxLength = ScreenWidth; } if(bCenterMsg) { x = (ScreenWidth-MaxLength)/2; y = (ScreenHeight-NumLines)/2; } else { if(xLeft) { x = *xLeft; } else { x = 1; } if(yTop) { y = *yTop; } else { y = 3; } } } for (i=0; i *MaxLength) { *MaxLength = Length; } LineLength[NumLines] = Length; ++NumLines; Length = 0; p += 2; LineText[NumLines] = p; } else { ++Length; ++p; if (*p == '\0') { // // End of the message. // if (Length > *MaxLength) { *MaxLength = Length; } LineLength[NumLines] = Length; ++NumLines; } } } *Lines = NumLines; }