Get started programming with DOS conio | Opensource.com

Get started programming with DOS conio

Create various practical and exciting applications by programming with conio.

Person using a laptop
x

Subscribe now

Get the highlights in your inbox every week.

One of the reasons so many DOS applications sported a text user interface (or TUI) is because it was so easy to do. The standard way to control console input and output (conio) was with the conio library for many C programmers. This is a de-facto standard library on DOS, which gained popularity as implemented by Borland's proprietary C compiler as conio.h. You can also find a similar conio implementation in TK Chia's IA-16 DOS port of the GNU C Compiler in the libi86 library of non-standard routines. The library includes implementations of conio.h functions that mimic Borland Turbo C++ to set video modes, display colored text, move the cursor, and so on.

For years, FreeDOS included the OpenWatcom C Compiler in the standard distributions. OpenWatcom supports its own version of conio, implemented in conio.h for particular console input and output functions, and in graph.h to set colors and perform other manipulation. Because the OpenWatcom C Compiler has been used for a long time by many developers, this conio implementation is also quite popular. Let's get started with the OpenWatcom conio functions.

Setting the video mode

Everything you do is immediately displayed on-screen via hardware. This is different from the ncurses library on Linux, where everything is displayed through terminal emulation. On DOS, everything is running on hardware. And that means DOS conio programs can easily access video modes and leverage screen regions in ways that are difficult using Linux ncurses.

To start, you need to set the video mode. On OpenWatcom, you do this with the _setvideomode function. This function takes one of several possible values, but for most programs that run in color mode in a standard 80x25 screen, use _TEXTC80 as the mode.

#include <conio.h>
#include <graph.h>

int
main()
{
  _setvideomode(_TEXTC80);
  … 

When you're done with your program and ready to exit back to DOS, you should reset the video mode back to whatever values it had before. For that, you can use _DEFAULTMODE as the mode.

 _setvideomode(_DEFAULTMODE);
  return 0;
}

Setting the colors

Every PC built after 1981's Color/Graphics Adapter supports 16 text colors and 8 background colors. Background colors are addressed with color indices 0 through 7, and text colors can be any value from 0 to 15:

  0 Black   8 Bright Black
  1 Blue   9 Bright Blue
  2 Green   10 Bright Green
  3 Cyan   11 Bright Cyan
  4 Red   12 Bright Red
  5 Magenta   13 Bright Magenta
  6 Brown   14 Yellow
  7 White   15 Bright White

You can set both the text color and the color behind it. Use the _settextcolor function to set the text "foreground" color and _setbkcolor to set the text "background" color. For example, to set the colors to yellow text on a red background, you would use this pair of functions:

 _settextcolor(14);
 _setbkcolor(4);

Positioning text

In conio, screen coordinates are always row,col and start with 1,1 in the upper-left corner. For a standard 80-column display with 25 lines, the bottom-right corner is 25,80.

Use the _settextposition function to move the cursor to a specific screen coordinate, then use _outtext to print the text you want to display. If you've set the colors, your text will use the colors you last defined, regardless of what's already on the screen.

For example, to print the text "FreeDOS" at line 12 and column 36 (which is more or less centered on the screen) use these two functions:

  _settextposition(12, 36);
  _outtext("FreeDOS");

Here's a small example program:

#include <conio.h>
#include <graph.h>

int
main()
{
    _setvideomode(_TEXTC80);

    _settextcolor(14);
    _setbkcolor(4);

    _settextposition(12, 36);
    _outtext("FreeDOS");

    getch();

    _setvideomode(_DEFAULTMODE);

    return 0;
}

Compile and run the program to see this output:

Text windows

The trick to unleashing the power of conio is to leverage a feature of the PC video display where a program can control the video hardware by region. These are called text windows and are a really cool feature of conio.

A text window is just an area of the screen, defined as a rectangle starting at a particular row,col and ending at a different row,col. These regions can take up the whole screen or be as small as a single line. Once you define a window, you can clear it with a background color and position text in it.

To define a text window starting at row 5 and column 10, and extending to row 15 and column 70, you use the _settextwindow function like this:

  _settextwindow(5, 10, 15, 70);

Now that you've defined the window, any text you draw in it uses 1,1 as the upper-left corner of the text window. Placing text at 1,1 will actually position that text at row 5 and column 10, where the window starts on the screen.

You can also clear the window with a background color. The _clearscreen function does double duty to clear either the full screen or just the window that's currently defined. To clear the entire screen, give the value _GCLEARSCREEN to the function. To clear just the window, use _GWINDOW. With either usage, you'll fill that region with whatever background color you last set. For example, to clear the whole screen with cyan (color 3) and a smaller text window with blue (color 1) you could use this code:

  _setbkcolor(3);
  _clearscreen(_GCLEARSCREEN);
  _settextwindow(5, 10, 15, 70);
  _setbkcolor(1);
  _clearscreen(_GWINDOW);

This makes it really easy to fill in certain areas of the screen. In fact, defining a window and filling it with color is such a common thing to do that I often create a function to do both at once. Many of my conio programs include some variation of these two functions to clear the screen or window:

#include <conio.h>
#include <graph.h>

void
clear_color(int fg, int bg)
{
  _settextcolor(fg);
  _setbkcolor(bg);
  _clearscreen(_GCLEARSCREEN);
}

void
textwindow_color(int top, int left, int bottom, int right, int fg, int bg)
{
  _settextwindow(top, left, bottom, right);
  _settextcolor(fg);
  _setbkcolor(bg);
  _clearscreen(_GWINDOW);
}

A text window can be any size, even a single line. This is handy to define a title bar at the top of the screen or a status line at the bottom of the screen. Again, I find this to be such a useful addition to my programs that I'll frequently write functions to do it for me:

#include <conio.h>
#include <graph.h>

#include <string.h>                    /* for strlen */

void
clear_color(int fg, int bg)
{
  … 
}

void
textwindow_color(int top, int left, int bottom, int right, int fg, int bg)
{
  … 
}

void
print_header(int fg, int bg, const char *text)
{
  textwindow_color(1, 1, 1, 80, fg, bg);

  _settextposition(1, 40 - (strlen(text) / 2));
  _outtext(text);
}

void
print_status(int fg, int bg, const char *text)
{
  textwindow_color(25, 1, 25, 80, fg, bg);

  _settextposition(1, 1);
  _outtext(text);
}

Putting it all together

With this introduction to conio, and with the set of functions we've defined above, you can create the outlines of almost any program. Let's write a quick example that demonstrates how text windows work with conio. We'll clear the screen with a color, then print some sample text on the second line. That leaves room to put a title line at the top of the screen. We'll also print a status line at the bottom of the screen.

This is the basics of many kinds of applications. Placing a text window towards the right of the screen could be useful if you were writing a "monitor" program, such as part of a control system, like this:

#include <conio.h>
#include <graph.h>

int
main()
{
  _setvideomode(_TEXTC80);

  clear_color(7, 1);                   /* white on blue */
  _settextposition(2, 1);
  _outtext("test");

  print_header(0, 7, "MONITOR");       /* black on white */

  textwindow_color(3, 60, 23, 79, 15, 3);       /* br white on cyan */
  _settextposition(3, 2);
  _outtext("hi mom");

  print_status(0, 7, "press any key to quit...");       /* black on white */
  getch();

  _setvideomode(_DEFAULTMODE);

  return 0;
}

Having already written our own window functions to do most of the repetitive work, this program becomes very straightforward: clear the screen with a blue background, then print "test" on the second line. There's a header line and a status line, but the interesting part is in the middle where the program defines a text window near the right edge of the screen and prints some sample text. The getch() function waits for the user to press a key on the keyboard, useful when you need to wait until the user is ready:

We can change only a few values to completely change the look and function of this program. By setting the background to green and red text on a white window, we have the start of a solitaire card game:

#include <conio.h>
#include <graph.h>

int
main()
{
  _setvideomode(_TEXTC80);

  clear_color(7, 2);                   /* white on green */
  _settextposition(2, 1);
  _outtext("test");

  print_header(14, 4, "SOLITAIRE");    /* br yellow on red */

  textwindow_color(10, 10, 17, 22, 4, 7);       /* red on white */
  _settextposition(3, 2);
  _outtext("hi mom");

  print_status(7, 6, "press any key to quit...");       /* white on brown */
  getch();

  _setvideomode(_DEFAULTMODE);

  return 0;
}

You could add other code to this sample program to print card values and suits, place cards on top of other cards, and other functionality to create a complete game. But for this demo, we'll just draw a single "card" displaying some text:

You can create other effects using text windows. For example, before drawing a message window, you could first draw a black window that's offset by one row and one column. The text window will appear to create a shadow over that area of the screen to the user. And we can do it all by changing only a few values in our sample program:

#include <conio.h>
#include <graph.h>

int
main()
{
  _setvideomode(_TEXTC80);

  clear_color(7, 1);                   /* white on blue */
  _settextposition(2, 1);
  _outtext("test");

  print_header(15, 3, "PROGRAMMING IN CONIO");  /* br white on cyan */

  textwindow_color(11, 36, 16, 46, 7, 0);       /* shadow */
  textwindow_color(10, 35, 15, 45, 7, 4);       /* white on red */
  _settextposition(3, 2);
  _outtext("hi mom");

  print_status(0, 7, "press any key to quit...");       /* black on white */
  getch();

  _setvideomode(_DEFAULTMODE);

  return 0;
}

You often see this "shadow" effect used in DOS programs as a way to add some visual flair:

The DOS conio functions can do much more than I've shown here, but with this introduction to conio programming, you can create various practical and exciting applications. Direct screen access means your programs can be more interactive than a simple command-line utility that scrolls text from the bottom of the screen. Leverage the flexibility of conio programming and make your next DOS program a great one.

Download the conio cheat sheet

As you explore programming with conio, it's helpful to have a list of common functions close at hand. I've created a double-sided cheat sheet with all the basics of conio, so download it and use it on your next conio project.
 

Person using a laptop

Use ncurses in Linux to place text at specific locations on the screen and enable more user-friendly interfaces.

About the author

photo of Jim Hall
Jim Hall - Jim Hall is an open source software advocate and developer, best known for usability testing in GNOME and as the founder + project coordinator of FreeDOS. At work, Jim is CEO of Hallmentum, an IT executive consulting company that provides hands-on IT Leadership training, workshops, and coaching.