Pick your Look:     Standard - Rising Blue - Retro Curves

C In DOS :: How to include a progress bar in C programs ?

This was one of the questions i was pondering on when i started writing quite a few interactive programs .The problem arised because the OS was a non multithreaded one ( DOS ) . I came up with several ways for doing this . I will try to pesent some of them in the following discussion .

First step in this was started thru a very simple program consider the following program :

#include<stdio.h>
#include<conio.h>
#include<dos.h>
main()
{
    int m,kbhit(void);
    clrscr();
    while(!kbhit())
    {
        printf("\b\\");
        delay(100);    /* Delay is defined in Dos.h */
        printf("\b|");   
        delay(100);  /* U might not have this if u use Borland C */
        printf("\b/");
        delay(100);
        printf("\b-");
        delay(100);
    }
return;     /* Keep the compiler happy */
}

It would give an illusion that a stick is rotating on the screen . I wanted this to appear on screen while the program was doing something usefull .
This could be implemented in a program having a main looping structure very easily consder a function

void rotate(void)
{
    static short index=0;
    printf(" ");
    switch(index)
    {
        case 0 : printf("\b\\");
            index++;
            break;
        case 1 : printf("\b|");
            index++;
            break;
        case 2 : printf("\b/");
            index++;
            break;
        case 3 : printf("\b-");
            index = 0;
            break;
    }
}

We would calll this function every time we go into loop ( especially if the body of loop is really big ..as in

void rotate(void);

while ( /* Some Expresseion */ )
{
      /* Body of loop */
      rotate();
}

So after meddling with some basic examples we are ready to meet some more pr-bars ( Progress bars from now on I'll use the term pr-bar as synonym for progress bar ) .

The previous version of our pr-bar had a big drawback . If the looping structure had to interact with user and it made use of monitor ( console ) for this purpose , our function would clutter up the entire screen with one of  \ , | ,/ ,- char acters ! So we would improve our function for this purpose . We would use a gotoxy() before we entered the actual body of the loop . That is we would ensure that the cursor was in the position required before we actually wrote something on screen .

So here is the improved version of pr-bar

void rotate(int x,int y)
{
    int current_x,current_y;
    static short index=0;
    printf(" ");
    current_x = wherex();
    current_y = wherey();
    gotoxy(x,y);
    switch(index)
    {
        case 0 : printf("\\");
            index++;
            break;
        case 1 : printf("|");
            index++;
            break;
        case 2 : printf("/");
            index++;
            break;
        case 3 : printf("-");
            index = 0;
            break;
    }
    gotoxy(current_x,current_y);
}

But what would we do if we did not have a looping structure at all in our program ... For this we first need to know what are Interrupts and Interrupt service Routines (ISR s) for short

Armed with the knowledge of interrupts and ISR s we now have different ways of writing pr-bars . One way ( The best way ) is to capture Timer interrupt .

Timer interrupt occurs each time the hardware clock in our computers tick . This happens 18.2 times a second . I mean  to say that the timer interrupt gets called 182 times every 10 seconds or 1092 times every minute . The interrupt number for timer interrupt is 8 . So if we wish to make our stick change its position every 1/2 second (say) we would wait for the interrupt to be called 6 times ( approx. 1/2 second ) . Then we would display the pr-bar . We would accomplish by the following code

#include<stdio.h>
#include<dos.h>

void interrupt our_timer();
void interrupt (*previous_timer)();

void main()
{

    /*Store previous ISR*/
    (*previous_timer)() = getvect(0x8);

    /*Capture timer interrupt*/
      setvect(0x8,our_timer);       

    /* Body of our program goes here */

    /*Restore original ISR before quitting */
    setvect(0x8,previous_timer);
}

void interrupt our_timer()
{
    static int ticks=0,    /*Number of ticks */
        active = 0; /*Weather rotate() active ? */
    ticks++;        /*Increment each time ISR is called */
    (*previous_timer)(); /* Call the previous ISR */
    if(ticks == 6 && !active)
    {
        ticks = 0;    /*reset*/
        active = 1;     /*Set active flag*/
        rotate(x,y);    /*Call the function rotate*/
        active = 0;    /*reset active flag*/
    }
}

The rotate() function is usual here . I have excluded it . Here the program is self explanatory .

Last program had an uncertain pr-bar , The number of rotations of the pr-bar depended on the computer ( basically its speed ) . Though this method works fine , we sometimes need to know how may rotations take place . This becomes obvious if we think of a situation where we show the amount of work completed . Say like 10% complete , 20 % complete etc.

This complicates the situation . There is no 'perfect' method to achieve this as far as I know. One of the methods is what I call 'evaluation method' { not the kind of evaluation version software ;) } . This method has a few requirements ,

  1. Your program has to have a loop or a group of  loops ( which are predominantly responsible for CPU usage , i.e, the CPU time required by the code outside loop(s) including the expression evaluated before entering the loop ,should be far less than the CPU usage of code within loop ) . This method usually cant be implemented on Linear programs.
  2. All the looping structure ( at least major ones ) should have definite number of iterations.
  3. Number of iterations of each loop must be far greater than unity . (the greater the better)

Evaluation technique consists of calculating the time required to execute the code in individual looping structures once . Then the time required by one iteration of each loop is multiplied by the number of iterations of that loop to get the total time required (approx.) by the loop . The approximate time required by the program is determined by adding the time required by all the loops. Now using timer interrupt we can display 'certain' pr-bar. An  example program follows

#include<stdio.h>
#include<dos.h>

void interrupt our_timer();
void interrupt previous_timer();

struct
{
    unsigned for_called : 1;    /*Has 'for' loop been called once */
    unsigned while_called : 1;    /*ditto for 'while' loop */
    unsigned start_time :1;         /*Start counting ticks*/
    unsigned end_time : 1;        /*End counting ticks*/
    unsigned start_prbar : 1;
}
flag        /* Flags for communication with timer */
    ={   
        0,
        0,
        0,
        0,
        0
    };

long ticks,forticks,whileticks;    /* Probably the contents could go > int */

int active;

main()
{

    /*Store previous ISR*/
    (*previous_timer)() = getvect(0x8);

    /*Capture timer interrupt*/
    setvect(0x8,our_timer);

    for(/*Some expression*/,/*Some expression*/,/*Some expression*/)
    {
for_loop :

        /* number of iterations = nfor */
       
        if(!flag.for_called)
        {
            ticks = 0;
            flag.start_time = 1;
        }
       
        /*

            Body of the loop

        */
       
        if(!flag.for_called)
        {
            flag.end_time = 1;
            flag.start_time = 0;
            flag.for_called = 1;
            forticks = ticks;
            goto while_loop;
        }
    }
   
    while(/*Some expression*/)
    {

while_loop :
        /* Number of iterations = nwhile */        

        if(!flag.while_called)
        {
            ticks = 0;
            flag.start_time = 1;
        }

        /*
   
            Body of loop

        */

        if(!flag.while_called)
        {
            flag.end_time = 1;
            flag.start_time = 0;
            flag.for_called = 1;
            whileticks = ticks;
            flag.start_prbar = 1;
            goto for_loop;
        }
    }

    /*Restore original ISR before quitting */
    setvect(0x8,previous_timer);
}           

#define ten_percent ( time / 10 )

int req_time ;

void interrupt our_timer()
{
    static int elapsed;

    if(flag.start_timer)
        if(!flag.end_timer)
            ticks+ +
    (*previous_timer)();

    if(flag.start_prbar)
    {
        req_time = (nfor*forticks)+(nwhile*whileticks),
        elapsed = forticks + whileticks;
        prbar (elapsed);
        flag.start_prbar = 0;
    }
    if(ticks == ten_percent && !active)
    {
        active = 1;

        prbar(ten_percent);
   
        active = 0;
    }
}

#define x 10
#define y 10

prbar(elapsed)
{
    static current=0;
    current += elapsed;
    gotoxy(x,y);
   
    printf("%d %% Complete",(current/req_time)*100);

}   
      

As you can see the code is not structured , it makes use of gotos,global variables etc . But these make the above program simple or even a ten page code could result . No wonder if the program on ending said 102% complete or 97% complete because as I said earlier the code if not perfect so you could include additional code for ensuring that this isnt the case. You could improve the pr-bar if want as in indicato.c in the cool ones category .Another limitation is the for loop and the while loop must be compatible ( We are using goto s remember? )

There is a somewhat more elegant way of finding the time required , That is by using standard library functions . But the problem is the probable incompatibility with the formats of time between the library function and the timer interrupt . Try writing your own pr-bar using library function . According to me The 'uncertain' pr-bar is the best , hassle free form of pr-bar  :D

You would agree the 'certain' pr-bars slow down your program by certain extent.

So that concludes my rant on how to create progress bars in non multithreaded environment.

Comments (10)

1. Windows XP Vs TSRs in C

Why isn't my TSR programs working under WindowsXP?
I use IBM compatible PC with Intel core2Duo processor...

An article in the web stated that windowsXP will
overwrite the original ROM-BIOS interrupts; Is it correct?

Mithun.R.Chuckraverthy on 12th April 2008

2. for 6th sem

thanks give a suggestion

Ravindra M on 19th January 2008

3. singh

just i want to know why we use #include<studio.h> and #include<dos.h> in c++.

Dhiraj kumar singh on 9th March 2007

4. i want c/c++ software projects

i want to do software project. iwant to get the idea about ..............

vinodsingh_katewal on 26th February 2007

5. Happy to know

Very nice

Yogesh Ldoha on 2nd February 2007

6. I need more fast timer trigger.

The timer triggers 18.2 times in a second. But sometimes it is required that timer triggers every mili seconds.

I think it is possible by writing some values in some register. Please help me. (kaustoov@yahoo.com)

Kaustoov Pal on 8th November 2005

7. Getting System information in C

i am developing the project in C ,and can u give the code for getting the system information in c like processor info,hdd info,ports info.....

Vijay on 28th October 2005

8. progress bar

it is useful for projects

jayaram peggem on 27th April 2005

9. basic of c language

we are need basic c language.

murugan on 21st January 2005

10. Use <pre> tags

Hi,

It will be really helpful if you use <pre></pre> tags to display the code as it would be much more readable.

;)

Mahendra Kalkura on 20th September 2004

  • There were some errors in your input

Add Article Comment

The CAPTCHA image