Playing with the Terminal

I miss the bad old days, back when I first learned to program. The microcomputers of the day were single user, single task. The hardware was directly accessible. You can truly do some messing around, which was quite entertaining for a budding programmer.

One of those early tasks I attempted on my original IBM PC was to plop an asterisk into the center of the screen. The computer then paused for a second. The program quit. If I were asked to write such a program today in Linux, I’d use the Ncurses library. Cinchy. But such a task is also possible to perform by using tricks revealed in this blog. Here are the relevant items:

  1. Discover the terminal’s row and column dimensions
  2. Deactivate line buffering for standard output
  3. Use ANSI codes to clear the screen and position the cursor
  4. Pause for one second
  5. Use ANSI codes to home the cursor

To reveal the terminal’s row and column dimensions, I referred to this previous post. The ioctl() function handily obtains data from the current terminal window.

Standard output is normally line buffered, which means data isn’t output until the buffer is full or a newline is encountered in the stream. You can use the setvbuf() function to disable line buffering for stdout, which means any character output appears immediately.

I cover ANSI codes to manipulate the screen and the cursor in several posts: Clear the Screen and manipulate the cursor among them.

To pause program execution, you can use the sleep() function, but it’s limited to one second intervals. But in this old post, I introduce my delay() function. It uses the clock() function to obtain values in milliseconds, which are used to create a delay.

Combining all these programming tidbits, I was able to cobble together the following program that sticks an asterisk in the center of the terminal screen and pauses one second.

2024_09_28-Lesson.c

#include <stdio.h>
#include <time.h>                *
#include <unistd.h>
#include <sys/ioctl.h>

#define home() printf("\e[H")
#define clear() printf("\e[H\e[2J")

/* set the cursor to position x (columns)
   and y (rows ) */
void locate(x,y)
{
    printf("\e[%d;%dH",y,x);
}

/* pause for m milliseconds */
void delay(int m)
{
    long pause;
    clock_t now,then;

    pause = m*(CLOCKS_PER_SEC/1000);
    now = then = clock();
    while( (now-then) < pause )
        now = clock();
}

/* set an asterisk in the center of the screen */
int main()
{
    int rows,columns;
    struct winsize w;
    char buffer[BUFSIZ];

    /* obtain the window size */
    ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
    rows = w.ws_row;
    columns = w.ws_col;

    /* remove line buffering */
    setvbuf(stdout,buffer,_IONBF,BUFSIZ);

    /* position the asterisk */
    clear();
    locate(columns/2,rows/2);
    putchar('*');

    /* wait 1 second */
    delay(1000);
    /* home the cursor */
    home();

    return 0;
}

The home() and clear() functions are macros that output the proper ANSI sequence to home the cursor and to clear the screen, respectively.

The locate() function uses ANSI sequences to position the cursor at a specific row and column.

The delay() function was lifted from an older post.

Within the main() function, I obtain the window size with a call to the ioctl() function. Then I use setvbuf() to disable line buffering for the standard output device, making it unbuffered.

The rest of the code positions the cursor, outputs the asterisk, and waits one second.

This code is just a warmup. For next week’s Lesson, I do some screen drawing that I would have otherwise though only possible when using a screen manipulating tool like the Ncurses library.

Leave a Reply