I write a lot of console programs to test simple concepts, containing a few test routines and a few Writeln commands. To me, this is much more convenient than having to write a VCL program where the output goes to a ListBox or a Memo.The problem with the console windows in Delphi is, that they close immediately after they are finished. So most of my console programs contain a Readln command as the last instruction in the program. Problem with Readln is that it is buffered, i.e. it waits until the return key (or Ctrl+Z) is pressed. Any other key is simply displayed, but does not close the window.
To be able to wait for any key, I wrote a simple unit with a ReadKey command, like in the old Turbo Pascal Crt unit. I added a KeyPressed command, a TextColor routine, and so the Console unit was born. Now it emulates most of the functionality of the Crt unit. The single routines, constants and variables are discussed in the tables below. Everyone familiar with the Crt unit will recognize them immediately.
I tested the unit in Delphi 5 up to Delphi 2006. It contains a few $IFDEF clauses to make it compile in all of the Win32 versions in that range.
UPDATE: I removed SysUtils from the implementation section. It turned out it was only used for the Win32Platform variable, which is queried in a few routines. I wrote my own routine to get the platform ID and so SysUtils is not needed anymore, which can considerably reduce the size of console programs. My EchoC utility was reduced by more than half its size.
Copy the contents of the zip file to a directory of your choice, and open the .dpr file in Delphi. If necessary, make sure the compilation target is Delphi Win32. Build the program, and take care that the unit is compiled or copied to a directory in the compiler’s search path (typically to $(BDS)\lib or $(DELPHI)\lib). Now you can use it from any console program you write. Note that you will have to recompile it for every Delphi version you have.
The program itself demonstrates a few of the capabilities of the unit. You are free to enhance it.
Constants and variables
The unit contains the following constants and variables.
|Black = 0; Blue = 1; Green = 2; Cyan = 3; Red = 4; Magenta = 5; Brown = 6; LightGray = 7; DarkGray = 8; LightBlue = 9; LightGreen = 10; LightCyan = 11; LightRed = 12; LightMagenta = 13; Yellow = 14; White = 15;||Colour constants for TextColor and TextBackground.|
|Blink = 128;||Blink attribute, to be or-ed with one of the colours Black..LightGray in TextBackground.|
|BW40 = 0; CO40 = 1; BW80 = 2; CO80 = 3; Mono = 7; Font8x8 = 256; C40 = CO40; C80 = CO80;||Text mode constants for TextMode. Defined for compatibility only.|
|CheckBreak: Boolean;||Controls user termination of an application using the console window.
Currently not in use.
|LastMode: Word;||Each time TextMode is called, the current video mode is stored in LastMode. Also, LastMode is initialized at program startup to the then-active video mode.
Currently not in use.
|TextAttr: Byte;||Stores currently selected text attributes.|
|WindMin: Word; WindMax: Word;||Store the screen coordinates of the current window. These variables are set by calls to the Window procedure.
WindMin defines the upper left corner.
WindMax defines the lower right corner.
The X coordinate is stored in the low byte, and the Y coordinate is stored in the high byte. Note that these coordinates are zero-based.
Functions and procedures
The unit implements the following functions and procedures. Coordinates are all 1-based.
|procedure ClrEol;||Clears all the characters from the cursor position to the end of the line, in the current background colour.|
|procedure ClrScr;||Clears the screen or window in the currently set background colour and returns the cursor to the upper left corner.|
|procedure Delay(Millisecs: Integer);||Delays a specified number of milliseconds.|
|procedure DelLine;||Deletes the line containing the cursor.|
|procedure GotoXY(X, Y: Smallint);||Moves the cursor to the given coordinates within the virtual screen.|
|procedure HighVideo;||Selects high-intensity characters.|
|procedure InsLine;||Inserts an empty line at the cursor position.|
|function KeyPressed: Boolean;||Determines if a key has been pressed on the keyboard.|
|procedure LowVideo;||Selects low-intensity characters.|
|procedure NormVideo;||Selects the original text attribute read from the cursor location at startup.|
|procedure NoSound;||Turns off the computer’s internal speaker.|
|function ReadKey: Char;||Reads a character from the keyboard. If the key is an extended key, ReadKey returns #0. A subsequent call of ReadKey will return a Char with the ordinal of the key code.|
|procedure Sound(Frequency: Smallint);||Sounds a note on the internal speaker with the given frequency. Frequency should have a value in the range 37..32767
On Windows NT, 2000 or XP, the note will not sound indefinitely. A subsequent call to Delay is necessary to make the note sound at all.
|procedure Beep(Frequency: Smallint; Duration: Integer);||A combination of
|procedure TextBackground(Color: Byte); overload;||Selects the background color.|
|function TextBackground: Byte; overload;||Returns the background color.|
|procedure TextColor(Color: Byte); overload;||Selects the foreground character color.|
|function TextColor: Byte; overload;||Returns the foreground character color.|
|procedure TextMode(Mode: Word);||Selects a specific text mode.
In the current implementation, this does nothing.
|function WhereX: Integer;||Returns the X coordinate of the current cursor location.|
|function WhereY: Integer;||Returns the Y coordinate of the current cursor location.|
|procedure Window(Left, Top, Right, Bottom: Integer);||Defines a text window on the screen. All subsequent output will go to this window. To reset the window to the full screen buffer, call Window with a zero or negative Left parameter.|
On most console windows, the buffer for the console window can exceed the window, both in width and in height. That is why Window is not restricted to the screen. It can take the size of the entire buffer. This will of course require scrolling with the scroll bars, and may not be desired. That is why I supplied the functions ScreenWidth, ScreenHeight, BufferWidth and BufferHeight, to make it easy to calculate the window you want to set.
As long as a window is set that does not span the entire buffer, internal routines are used to display text, i.e. Writeln and friends will write via those routines. To reset the window to the entire buffer, you can either set Window(1, 1, BufferWidth, BufferHeight), or you can simply specify a first parameter of zero or below, e.g. Window(0, 0, 0, 0).
The internal routines will treat the characters #8 (backspace), #9 (tab), #10 (line feed), #12 (form feed) and #13 (carriage return) as special control characters. The original Crt unit does not handle #9 and #12 like that, if a window is set.
Sound, Delay, NoSound
on Windows NT, 2000 and XP, it is not so easy to turn on the internal speaker with Sound and let it run until you stop it with NoSound. Instead, I use the Windows.Beep function.
To make older example code run, I made two versions of Sound. One expects a frequency and a duration, and works like Windows.Beep. The other works more or less like in Turbo Pascal. You start by calling Sound(Frequency);. Because such calls are usually followed by a call to Delay, I actually only made Sound store the frequency and set a flag. Delay, if it sees the flag is set, now has all information to call Windows.Beep, which it does, and then resets the flag. So while it may look as if
123 124 125
Sound(400); Delay(200); NoSound;
actually first starts the speaker, then waits for 200 milliseconds and then stops the speaker again, in fact Sound only stores the value 400, and sets the ssPending flag. The speaker is not started yet. But Delay now sees the flag, reads the value 400, and together with the duration of 200, it can call
now. NoSound simply does nothing, in this case.
On Windows 95, 98 and ME, the routines directly access the ports, so Sound does actually start the speaker, Delay only waits, and NoSound turns the speaker off again.
Update: I turned the routines Sound, NoSound and Delay into procedural variables, and set these according to the OS. This way, the routines don’t have to check for the OS anymore. Only the startup of the unit will do that. I had to rename Sound(Frequency, Duration) to Beep, since procedural variables can of course not be overloaded.
These days, I don’t think it makes much sense to define and use text modes like 80×25 Mono or 40×25 mono or colour. So currently, TextMode simply does nothing.
The Console unit (console.zip) is freeware. All rights are reserved. Its code is provided as is, expressly without a warranty of any kind. You use it at your own risk.
I hope this code is useful to you. If you use some of it, please credit me. If you modify or improve the unit, please send me the modifications.
I may improve or enhance the unit myself, and I will try to post changes here. But this is not a promise. Please don’t request features.
Standard Disclaimer for External Links
These links are being provided as a convenience and for informational purposes only; they do not constitute an endorsement or an approval of any of the products, services or opinions of the corporation or organization or individual. I bear no responsibility for the accuracy, legality or content of the external site or for that of subsequent links. Contact the external site for answers to questions regarding its content.
Disclaimer and Copyright
The coding examples presented here are for illustration purposes only. The author takes no responsibility for end-user use. All content herein is copyrighted by Rudy Velthuis, and may not be reproduced in any form without the author's permission. Source code written by Rudy Velthuis presented as download is subject to the license in the files.