// // Copyright 2020 Electronic Arts Inc. // // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free // software: you can redistribute it and/or modify it under the terms of // the GNU General Public License as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed // in the hope that it will be useful, but with permitted additional restrictions // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT // distributed with this program. You should have received a copy of the // GNU General Public License along with permitted additional restrictions // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection /* $Header: g:/library/source/rcs/./windows.c 1.12 1994/05/20 15:35:25 joe_bostic Exp $ */ /*************************************************************************** ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** *************************************************************************** * * * Project Name : LIBRARY * * * * File Name : WINDOWS.C * * * * Programmer : Everyone * * * * Last Update : February 3, 1992 [DRD] * * * *-------------------------------------------------------------------------* * Functions: * * Change_New_Window -- Combined Change_Window and New_Window. * * Change_Window -- Changes the 'current' window in the system. * * Fetch_Char -- Gets one undipthonged char from input. * * Flush_Line -- Outputs the accumulate text line to screen. * * In_Char -- Stores (un-dipped) character(s) from input to buffer. * * New_Window -- Clears the current window to the background color. * * Set_More_Off -- Turns the 'more' prompting off. * * Set_More_On -- Turns the 'more' prompting on. * * Set_More_Prompt -- Adjusts the more prompt text for default routine * * Standard_More_Prompt -- Default more prompt code for Window_Print * * Window_Int_Print -- Prints an integer to the window. * * Window_Print -- Displays and wraps text into a window. * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include #include #include #include #include #include #include "ww_win.h" #include #include #include PRIVATE void Scroll_Window(void); PRIVATE void Flush_Line(void); PRIVATE void In_Char(char *str); PRIVATE char Fetch_Char(void); PRIVATE int ScrollCounter = 0; // Count of the lines displayed before a pause. PRIVATE char Line[84]; // Staging line buffer. PRIVATE int Pos; // Char Position of next free character. PRIVATE int PPos; // Pixel position of next free character. PRIVATE int WPos; // Char position in window. PRIVATE char *MainSource; PRIVATE char *AltSource; PRIVATE char Char[2]; PRIVATE char Stack; PRIVATE char WordWrapFlag = FALSE; // flag for a word wrap. PRIVATE int MoreSpace = 7; PRIVATE int MoreFColor = 0; PRIVATE int MoreBColor = 0; int WindowColumns=40; int WindowLines=25; int WindowWidth=40; unsigned int WinB=0; unsigned int WinC=1; unsigned int WinX=0; unsigned int WinY=0; unsigned int WinCx=0; unsigned int WinCy=0; unsigned int WinH=25; unsigned int WinW=40; unsigned int Window=0; int MoreOn = TRUE; char *TXT_MoreText = "--More--"; void (*Window_More_Ptr)(char const *,int,int,int) = Standard_More_Prompt; extern GraphicViewPortClass *LogicPage; /*************************************************************************** * STANDARD_MORE_PROMPT -- Default more prompt code for Window_Print * * * * This is the standard "" prompting code that is used by * * Window_Print when a page is full of text and a pause is desired * * before the next page of text is printed. This function is called * * through the Window_More_Ptr global. * * * * INPUT: prompt -- Pointer to ASCII text that will be window printed * * at the right margin. * * * * space -- The number of spaces to allow for the 'more' text. * * * * fcolor -- The foreground color to use for the 'more' text. * * * * bcolor -- The background oclor to use for the 'more' text. * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 07/29/1991 JLB : Created. * *=========================================================================*/ void Standard_More_Prompt(char const *prompt, int space, int fcolor, int bcolor) { int x, y, moresize; moresize = (space - 1) * (FontWidth+FontXSpacing); x = ((WinX+WinW) << 3) - moresize; //y = WinY + ((WinH/FontHeight)-1)*FontHeight; y = WinY + (WinCy-1) * (FontHeight+FontYSpacing); // Default "more" prompter. LogicPage->Print(prompt, x, y, fcolor ? fcolor : WindowList[Window][WINDOWBCOL], bcolor ? bcolor : WindowList[Window][WINDOWFCOL]); //BG if (LogicPage == SEENPAGE) { //BG Window_Show_Mouse(); //BG } // PWG - have to figure out how to do this in windows library // Clear_KeyBuffer(); // Get_Key(); //BG if (LogicPage == SEENPAGE) { //BG Window_Hide_Mouse(Window); //BG } // Erase the more prompt prompt. // Text_Print(prompt, x, y, WinB, WinB); LogicPage->Fill_Rect(x, y, x + moresize - 1, y + (FontHeight+FontYSpacing) - 1, (unsigned char)WinB); } /*************************************************************************** * SET_MORE_PROMPT -- Adjusts the more prompt text for default routine * * * * Use this routine to control the text of the "" prompt that * * the default more prompt routine uses. This can be useful for * * foreign language translations. * * * * INPUT: prompt -- Pointer to ASCII text that will be window printed * * at the right margin. * * * * space -- The number of spaces to allow for the 'more' text. * * * * fcolor -- The foreground color to use for the 'more' text. * * * * bcolor -- The background color to use for the 'more' text. * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 07/29/1991 JLB : Created. * *=========================================================================*/ void Set_More_Prompt(char const *prompt, int space, int fcolor, int bcolor) { if (prompt) { TXT_MoreText = (char*)prompt; MoreSpace = space; MoreFColor = fcolor; MoreBColor = bcolor; } else { TXT_MoreText = ""; MoreSpace = 7; MoreFColor = MoreBColor = 0; } } /*************************************************************************** * SET_MORE_ON -- Turns the 'more' prompting on. * * * * Use this routine to turn on the 'more' prompting that Window_Print * * does. If you have a custom more function pointer, then that * * routine will be called, otherwise the library default 'more' prompt * * will be utilized. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 07/25/1991 JLB : Created. * *=========================================================================*/ void Set_More_On(void) { MoreOn = TRUE; ScrollCounter = 0; } /*************************************************************************** * SET_MORE_OFF -- Turns the 'more' prompting off. * * * * This routine will turn the 'more' prompting that Window_Print does * * off. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 07/25/1991 JLB : Created. * *=========================================================================*/ void Set_More_Off(void) { MoreOn = FALSE; } /*************************************************************************** * CHANGE_WINDOW -- Changes the 'current' window in the system. * * * * Use this routine to change the 'current' window. The current window * * is used in Window_Print and some other graphic output routines. * * * * INPUT: windnum -- The window number to change to. * * * * OUTPUT: Returns with the previously current window. * * * * WARNINGS: none * * * * HISTORY: * * 07/25/1991 JLB : Created. * *=========================================================================*/ int Change_Window(int windnum) { int oldwindow; int *data; oldwindow = Window; Window = windnum; data = &WindowList[windnum][0]; WinX = *data++; WinY = *data++; WinW = *data++; WinH = *data++; WinC = *data++; WinB = *data++; WinCx = *data++; WinCy = *data++; ScrollCounter = 0; WPos = WinCx / (FontWidth+FontXSpacing); WindowLines = (WinH-FontYSpacing) / (FontHeight+FontYSpacing); WindowWidth = WinW << 3; WindowColumns = WindowWidth / (FontWidth+FontXSpacing); return (oldwindow); } /*************************************************************************** * CHANGE_NEW_WINDOW -- Combined Change_Window and New_Window. * * * * This is a combo-routine. It merely combines the Change_Window * * with the New_Window routines. It will save some (small) code if * * you use this routine instead of the two function calls. * * * * INPUT: window -- Window number to change to and clear. * * * * OUTPUT: Returns with the previously current window. * * * * WARNINGS: none * * * * HISTORY: * * 07/25/1991 JLB : Created. * *=========================================================================*/ int Change_New_Window(int windnum) { int oldwindow; oldwindow = Change_Window(windnum); New_Window(); return(oldwindow); } /*************************************************************************** * NEW_WINDOW -- Clears the current window to the background color. * * * * This routine clears the current window to the background color. It * * is used in preparation to Window_Print because it ensures a clean * * 'slate' for the text. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 07/25/1991 JLB : Created. * *=========================================================================*/ void New_Window(void) { int x,y,w,h; x = WinX << 3; y = WinY; w = (WinX + WinW) << 3; h = WinY + WinH; LogicPage->Fill_Rect(x, y, w - 1, h - 1, (unsigned char)WinB); WinCx = WPos = 0; WinCy = 0; ScrollCounter = 0; } /*************************************************************************** * WINDOW_INT_PRINT -- Prints an integer to the window. * * * * Use this routine to print an integer to the window. This routine * * as all other Window printing routines will handle word wrap. * * * * INPUT: num -- The integer to convert to ASCII and print. * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 07/25/1991 JLB : Created. * *=========================================================================*/ void Window_Int_Print(int num) { Window_Print("%d", num); } /*************************************************************************** * WINDOW_PRINT -- Displays and wraps text into a window. * * * * This is the general purpos text output routine that will handle * * word wrap within a window. It is useful for displaying arbitrary * * text. This routine will handle dipthonged text and as such it * * can be quite useful in saving memory. * * * * INPUT: string -- String to print. This can be of ANY length and * * can even contain some formatting codes. The * * codes supported are: * * * * KA_SETX Forces the cursor X position to the value * * specified. * * * * KA_SETY Forces the cursor Y position to the value * * specified. * * * * KA_MORE Causes an immediate "" prompt * * regardless of the scroll situation. * * * * KA_RETURN Breaks line and continues output at the * * left edge of following line. * * * * * * KA_FORMFEED Clears the window and continues printing at * * the upper left corner. * * * * KA_SETBKGDCOL Set the background color with the color * * specified by the following byte. * * * * * * KA_SETFORECOL Set the foreground color with the color * * specified by the following byte. * * * * KA_TAB Move the cursor over to the next tabstop. * * Tabstops are set every 8 columns. * * * * KA_SPCTAB Insert spaces until the cursor is positioned * * at the next tabstop. * * * * %s Replace the "%s" with the text pointed to * * by the pointer argument passed to the * * routine (follows same method a printf). * * * * %d Replace the "%d" with an integer ASCII * * number of the int passed to the routine. * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 07/25/1991 JLB : Created. * * 07/29/1991 JLB : Added MORE, SETX, and SETY * *=========================================================================*/ void Window_Print(char const string[], ...) { int oldcx, x, y; // Scratch variables. char c; // Current character. char buffer[10]; // Working %d buffer. int old_c, old_b; // Original window colors. va_list arg; // Argument list var. va_start(arg, string); WordWrapFlag = FALSE; // initialize word wrap flag. Pos = PPos = 0; Line[0] = '\0'; Char[0] = Char[1] = 0; MainSource = (char*)&string[0]; AltSource = NULL; old_c = WinC; old_b = WinB; //BG if (LogicPage == SEENPAGE) { //BG Window_Hide_Mouse(Window); //BG } while (TRUE) { c = Fetch_Char(); if (!c) break; // Exit on NULL character. /* ** Substitution commands only work if not already expanding a ** string. */ if (!AltSource) { if (c == '%') { switch(tolower(Char[0])) { case 's': AltSource = va_arg(arg, char*); if (AltSource) { Stack = Char[1]; Char[0] = Char[1] = '\0'; c = Fetch_Char(); } break; case 'd': AltSource = buffer; sprintf(buffer, "%d", va_arg(arg, int)); Stack = Char[1]; Char[0] = Char[1] = '\0'; c = Fetch_Char(); break; default: break; } } } switch(c) { #if(FALSE) // these are the positions of foreign language characters /* ** These are characters that shouldn't be window printed because ** they are currently reserved. */ case KA_CTRL_C: case KA_CTRL_D: case KA_CTRL_E: case KA_CTRL_G: case KA_CTRL_J: case KA_CTRL_K: case KA_CTRL_N: case KA_CTRL_O: case KA_CTRL_P: case KA_CTRL_Q: case KA_CTRL_R: case KA_CTRL_T: case KA_CTRL_U: case KA_CTRL_V: case KA_CTRL_W: case KA_CTRL_Z: case KA_CTRL_BACKSLASH: case KA_CTRL_CARROT: case KA_CTRL_UNDERLINE: break; #endif /* ** Force cursor column to specified X value. */ case KA_SETX: Flush_Line(); WPos = Fetch_Char(); WPos = MAX(0, WPos); // WPos is max width char position WPos = MIN(WindowColumns-1, WPos); WinCx = WPos * (FontWidth+FontXSpacing); break; /* ** Force the cursor to specified Y value. */ case KA_SETY: Flush_Line(); WinCy = Fetch_Char(); //WinCy = MAX(0, WinCy); WinCy = MIN((long)WindowLines-1, (long)WinCy); break; /* ** Force a "" prompt. */ case KA_MORE: Flush_Line(); if (Window_More_Ptr) { //BG if (LogicPage == SEENPAGE) Window_Show_Mouse(); Window_More_Ptr(TXT_MoreText, MoreSpace, MoreFColor, MoreBColor); //BG if (LogicPage == SEENPAGE) Window_Hide_Mouse(Window); } break; /* ** Clear and home the window cursor. This is the same ** as New_Window(). */ case KA_FORMFEED: New_Window(); break; /* ** Move cursor to start of next line. */ case KA_RETURN: Flush_Line(); ScrollCounter++; WinCx = 0; #if(FALSE) if (WinCy >= WindowLines-1) { Scroll_Window(); } else { WinCy++; } #else WinCy++; #endif break; /* ** Set the background color. */ case KA_SETBKGDCOL: Flush_Line(); WinB = Fetch_Char(); break; /* ** Set the foreground color. */ case KA_SETFORECOL: Flush_Line(); WinC = Fetch_Char(); break; /* ** Move cursor to next column. */ case KA_TAB: Flush_Line(); WPos = ((WPos + 8) & 0xFFF8) - 1; if (WPos >= WindowColumns) { WPos = 0; } WinCx = WPos * (FontWidth+FontXSpacing); break; /* ** Tab to specified column but add spaces. */ case KA_SPCTAB: Flush_Line(); oldcx = WinCx; x = WinX << 3; y = WinY + (WinCy * (FontHeight+FontYSpacing)); WPos = ((WPos + 8) & 0xFFF8) - 1; if (WPos >= WindowColumns) { WinCx = WPos = 0; // Fill_Rect instead of printing spaces LogicPage->Fill_Rect(x + oldcx, y, x + WindowWidth - 1, y + (FontHeight+FontYSpacing) - 1, (unsigned char)WinB); ScrollCounter++; WinCy++; } else { WinCx = WPos * (FontWidth+FontXSpacing); // Fill_Rect instead of printing spaces LogicPage->Fill_Rect(x + oldcx, y, x + WinCx - 1, y + (FontHeight+FontYSpacing) - 1, (unsigned char)WinB); } break; /* ** next character is a extended value 1-127, but 128 is added ** for a value 129-255 */ case KA_EXTEND: c = 127; // NOTE: this falls thru to the default case DO NOT MOVE!!!!! /* ** next character is a literal value 1-127, except 13 */ // case KA_LITERAL: // if (c != (char) 127) { // check if fell thru from extend case // c = 0; // set to zero for literal case // } // c += Fetch_Char(); // NOTE: this falls thru to the default case DO NOT MOVE!!!!! /* ** Normal character output. */ default: PPos += Char_Pixel_Width(c); // get pixel location of next char Line[Pos++] = c; Line[Pos] = '\0'; if (WinCx + PPos > (unsigned)WindowWidth) { Flush_Line(); } break; } } /* ** If there is text still pending, then display it before exiting. */ if (Pos) Flush_Line(); /* ** Record changes in the cursor position. */ WindowList[Window][WINDOWCURSORX] = WinCx; WindowList[Window][WINDOWCURSORY] = WinCy; /* ** Restore the window colors to their original values. */ WindowList[Window][WINDOWFCOL] = WinC = old_c; WindowList[Window][WINDOWBCOL] = WinB = old_b; //BG if (LogicPage == SEENPAGE) { //BG Window_Show_Mouse(); //BG } va_end(arg); } /*************************************************************************** * SCROLL_WINDOW -- Scrolls the text window up one line. * * * * This will scroll the text window up one line. It will handle any * * pausing for "more" if the MoreOn flag is set. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: This routine assumes that the LogicPage is the SEENPAGE. * * If this is not the case, the program may appear to hang * * if a "more" prompt is generated. * * * * HISTORY: * * 07/25/1991 JLB : Created. * *=========================================================================*/ PRIVATE void Scroll_Window(void) { int y; // Top pixel row of bottom line of window. /* ** Possibly prompt for more text. */ if (ScrollCounter >= WindowLines-1 && MoreOn) { ScrollCounter = 0; if (Window_More_Ptr) { //BG if (LogicPage == SEENPAGE) Window_Show_Mouse(); Window_More_Ptr(TXT_MoreText, MoreSpace, MoreFColor, MoreBColor); //BG if (LogicPage == SEENPAGE) Window_Hide_Mouse(Window); } } /* ** Scroll the window up one line. */ y = ((WinH / (FontHeight+FontYSpacing)) - 1) * (FontHeight+FontYSpacing); LogicPage->Blit(*LogicPage,WinX<<3, WinY + (FontHeight+FontYSpacing), WinX<<3, WinY, WinW<<3, WinH - (FontHeight+FontYSpacing) ); LogicPage->Fill_Rect(WinX<<3, WinY + y, ((WinX+WinW)<<3) - 1, WinY + WinH - 1, (unsigned char)WinB); } /*************************************************************************** * FLUSH_LINE -- Outputs the accumulate text line to screen. * * * * This will display the accumlated text line to the screen. It will * * handle breaking the text line at an appropriate position. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 07/25/1991 JLB : Created. * *=========================================================================*/ PRIVATE void Flush_Line(void) { int breakit, breaksize, breakwidth; int x, y; // Coordinates of text print. int breakpoint; // Point to break the line (if possible). char breakchar; // Break replace character. int index; // Backward moving index var. /* ** There could be a held and this is implied by the cursor Y position ** beyond the bottom of the window. If this is the case, then scroll the ** window and proceed with the line flush. */ while (WinCy >= (unsigned)WindowLines /*&& Pos > 0*/) { Scroll_Window(); if (WinCy >= (unsigned)WindowLines) WinCy--; } //if (WinCy >= WindowLines) WinCy = WindowLines-1; x = (WinX<<3) + WinCx; y = WinY + (WinCy*(FontHeight+FontYSpacing)); breakwidth = WindowWidth; // if (ScrollCounter >= WindowLines - 1 && MoreOn) { // breakwidth -= (MoreSpace * (FontWidth+FontXSpacing)); // use maximum font width // } /* ** Try to break the line at the last space IF the line has reached the edge ** of the window. */ breakpoint = Pos; breaksize = PPos; if (WinCx + breaksize > (unsigned)breakwidth) { /* ** Since the text WILL spill past the edge of the window, determine the ** point where the break should occur. If this line is ready for the ** prompt, then breaking must account for the text. */ if (ScrollCounter >= WindowLines - 1 && MoreOn) { breakwidth -= (MoreSpace * (FontWidth+FontXSpacing)); // use maximum font width } breakwidth -= WinCx; breakit = 0; for (index = breakpoint - 1; index > 0; index--) { breakchar = Line[index]; breaksize -= Char_Pixel_Width(breakchar); // only once, find largest text that can fit on the line if (!breakit) { // was this the char that went past the right edge if (breaksize <= breakwidth) { breakit = index; // save this position if there is no spaces } } // after largest text is found then look for a space to break on if (breakit && breakchar == KA_SPACE) { breakpoint = index; WordWrapFlag = FALSE; // word will start at beginning of next line break; } } /* ** Exception: When the current text buffer cannot be broken at a logical ** place AND the text is starting past the left margin, THEN there is ** an implied break between the previous text output and this one. ** Output the current text on the next line left margin. */ if (!index) { if (WinCx && !WordWrapFlag) { breakpoint = breaksize = 0; // Continue text on next line. WordWrapFlag = TRUE; // indicate a word continuation. } else { breakpoint = breakit; // Just print as much as possible. } } } breakchar = Line[breakpoint]; Line[breakpoint] = '\0'; LogicPage->Print(Line, x, y, WinC, WinB); WinCx += breaksize; // add size of text string printed. Line[breakpoint] = breakchar; if (breakchar == KA_SPACE) { // take out a space between words. breakpoint++; } // take out another space for double spacing after end of sentence. if (Line[breakpoint] == KA_SPACE) { breakpoint++; } strcpy(Line, &Line[breakpoint]); Pos = strlen(Line); PPos = String_Pixel_Width(Line); /* ** If at this point there is still text in the buffer, then flushing has ** not been completed. Scroll to next line and repeat the text flushing ** process. */ if (Pos || WinCx >= (unsigned)WindowWidth) { WinCx = WPos = 0; #if(FALSE) if (WinCy >= WindowLines-1) { Scroll_Window(); } else { WinCy++; } #else WinCy++; #endif Flush_Line(); ScrollCounter++; // must be done after flush line for correct counting } } /*************************************************************************** * IN_CHAR -- Stores (un-dipped) character(s) from input to buffer. * * * * Use this routine to fetch the next character from the input stream. * * If the character was dipthonged, then it will be broken into its * * component ASCII characters and stored in the specified location. * * This is the core character stream reading code. * * * * INPUT: str -- char pointer to the position to store the character(s)* * fetched from the input stream. * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 07/25/1991 JLB : Created. * *=========================================================================*/ PRIVATE void In_Char(char *str) { char c; // Character to return. char next; // Following character (if any). c = next = '\0'; /* ** Fetch a raw byte from the input stream. */ if (AltSource) { if (*AltSource == '\0') { AltSource = NULL; c = Stack; } else { c = *AltSource++; } } if (!c && MainSource) { if (*MainSource == '\0') { MainSource = NULL; } else { c = *MainSource++; } } /* ** Convert a dipthong character into it's component ** ASCII characters. */ if (c & 0x80) { c &= 0x7F; next = c & (char)0x07; c = (char)((c & (char)0x78) >> 3); next = Dipthong[c][next]; // Dipthong character. c = Common[c]; // Common character. } *str++ = c; *str = next; } /*************************************************************************** * FETCH_CHAR -- Gets one undipthonged char from input. * * * * This routine will fetch one character from the input stream. The * * character has already been un-dipthonged. It is a straight ASCII * * character. This routine ensures that if the next character in the * * input stream needs to be examined, it is available in Char[0]. * * * * INPUT: none * * * * OUTPUT: Returns next character in the input stream (ASCII). If NULL * * is returned, then this indicates the end of the input stream. * * * * WARNINGS: none * * * * HISTORY: * * 07/25/1991 JLB : Created. * *=========================================================================*/ PRIVATE char Fetch_Char(void) { char c; // Character to return. if (!Char[0]) { In_Char(&Char[0]); } c = Char[0]; Char[0] = Char[1]; Char[1] = '\0'; if (!Char[0]) { In_Char(&Char[0]); } return (c); }