Handling non-input keystrokes?

2019-09-19 06:21发布

问题:

I'm not sure exactly how to phrase what I'm trying to ask; in C++, using the stdio.h header instead of iostream, how would I make it so that if the escape key is pressed at any point, the program is terminated? Is there something I could add once at the top of the program, or would I have to add it to every loop/conditional individually? Below is my code (the sleep() function is just for a visual loading/calculating effect):

#include <stdio.h>
#include <math.h>
#include <windows.h>

void repeat();
void quadratic()
{
    double a, b, c;
    double ans[2];
    printf("-Arrange your equation in the form aX^2+bX+c \n-Enter the value of a: ");
    scanf("%lf", &a);
    printf("-Enter the value of b: ");
    scanf("%lf", &b);
    printf("-Enter the value of c: ");
    scanf("%lf", &c);
    double radical=((b*b)-(4*a*c));
    double root=sqrt(radical);
    double negB=(-1)*b;
    double denominator=2*a;
    if(denominator==0)
    {
        printf("Calculating");
        Sleep(100);
        printf(".");
        Sleep(100);
        printf(".");
        Sleep(100);
        printf(".");
        Sleep(100);
        printf("\nError: Denominator must be non-zero.\n \n \n");

    }
    else if(radical==0)
    {
        ans[0]=negB/denominator;
        printf("Both roots are equal: both values are X=%lf\n \n \n", ans[0]);

    }
    else if(radical<0)
    {
        printf("Calculating");
        Sleep(100);
        printf(".");
        Sleep(100);
        printf(".");
        Sleep(100);
        printf(".");
        Sleep(100);
        double r,i;
        radical*=-1;
        r=negB/(2*a);
        i=sqrt(radical)/(2*a);
        printf("\nBoth roots are imaginary numbers.\n");
        printf("Non-real answer(s): X=%lf+%lfi X=%lf-%lfi\n \n \n",r,i,r,i);

    }
    else
    {
    ans[0]=(negB+root)/denominator;
    ans[1]=(negB-root)/denominator;
    printf("Calculating");
        Sleep(100);
        printf(".");
        Sleep(100);
        printf(".");
        Sleep(100);
        printf(".");
        Sleep(100);
        printf("\nX=%lf, X=%lf\n \n", ans[0], ans[1]);
    }
    repeat();

}
void repeat()
{
    quadratic();
}
int main(void)
{   
    quadratic();
    return 0;
}

回答1:

The terminal used in stdio is likely to be line buffered (cooked). If it is, checking for the escape key through scanf will not work.

SEE THESE URLS:

Capture characters from standard input without waiting for enter to be pressed

scanf not reading input

CURSES or NCURSES will detect the escape key (ASCII character 27), depending on the terminal type.

This code can be used in WINDOWS to check for the ESCAPE key.

    #include <conio.h> 
    #include <ctype.h>

    int ch;

    _cputs( "Type 'Y' when finished typing keys: " );
    do
    {
      ch = _getch();
      ch = toupper( ch );

          if (ch != 27) {
           _cputs( "CHARACTER: " );
           _putch( ch );
           _putch( '\r' );    // Carriage return
           _putch( '\n' );    // Line feed
      }

    } while( ch != 27 );


回答2:

Is there something I could add once at the top of the program, or would I have to add it to every loop/conditional individually?

I think you can add something once, and use it to catch key stroke events easily throughout your program.

Following is a code snippet showing a function I have used to handle key stroke events in a console application. It uses GetAsyncKeyState(). Included is a section that shows how to capture a CTRL key, and how you can do something once you see it. (the snippet shown shows how I capture a <ctrl><shift><h> key sequence to display a help menu for using this particular routine.

Note: In the description, delay_x(float delay) is simply a custom, non-blocking sleep, or delay function that includes a call to the following snippet. It is called from within the main program loop while(1){...} . Exiting the program is provided in one of the keystroke combinations: <CTRL><SHIFT><K>

Code snippet:

/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
//
//  SetAppState() is called continuously from within delay_x()
//  to capture keystroke combinations as they occur asynchronously
//  with this application, Keystroke combinations are listed below
//
//  Note: GetAsyncKeyState() can maintian information regarding the 
//        state of a key instantaineously by use the MSB, 
//        and recently by using the LSB.  
//        
//        For this application
//        only instantaineous information will be kept, minimizing
//        conflicts with other keyboard shortcut definitions 
//        defined by other applications that may be running
//        simultaineously.
//  
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////

void SetAppState(void)
{
    short state=0;
    short state1=0;

    state = GetAsyncKeyState(VK_CONTROL);
    if (0x80000000 & state) //check instantaineous state of key
    {
        state = GetAsyncKeyState(VK_SHIFT); 
        if (0x80000000 & state) //check instantaineous state of key
        {
            state = GetAsyncKeyState('h'); 
            state1 = GetAsyncKeyState('H'); 
            if ((0x80000000 & state) || 
                (0x80000000 & state1))
            {    sprintf(gTempBuf, "Usage - keystrokes to access and control the PaAutoStartSlot application:\n\n"
                                   "<CTRL><SHIFT> H   (H)elp -    \n"
                                   "<CTRL><SHIFT> V  o(V)erride - \n"
                                   "<CTRL><SHIFT> S   (S)tatus -  \n"
                                   "<CTRL><SHIFT> K   (K)ill -    \n"
                                   "<CTRL><SHIFT> N   (N)o -      \n"
                                   "<CTRL><SHIFT> I   (I)Inside - \n"
                                   "<CTRL><SHIFT> O   (O)Outside- \n"
                                   "\nSee log file at this location for runtime errors: \n\n%s", LOGFILE); 
                 MessagePopup("Usage Menu",gTempBuf);
            }

///// ... more code ...
End of snippet  

Edit - Answer to questions in comments how to call GetAsyncKeyState()

There are many ways you could call GetAsyncKeyState() at the same time other stuff is going on. Threads are a good way. You can also do it all in line using a while()/switch(){} combination. Here is a very simple example of how you could do this (in pseudo code)

int gRunning = 1;
int state = 1; 

int main(void)
{

    //create variables, initialize stuff
    while(gRunning)//this is your main program loop
    {
        delay_x(1.0);//check for keystrokes
        switch(state)  {
            case 1:
                //do some stuff here  
                //and experiment with values passed to delay_x(n)
                delay_x(10000);//check for keystrokes
                state++;
                break;
            case 2:
                //do some different stuff here
                delay_x(10000);//check for keystrokes
                state++;
                break;
            ... Add as many cases as you need for your program.
            case n://last case, set execution flow to top
                //do some more different stuff here
                delay_x(10000);//check for keystrokes
                state = 1;//loop back to top
                break;
         }
     }
    return 0;
}
void delay_x (float delay)
{   
    static clock_t time1;   
    static clock_t time2;   clock();    

    time1 = clock();    
    time2 = clock();    

    while ((time2 - time1) < delay) 
    {       
        time2 = clock();        
        SetAppState(); //see partial definition in my original answer above.
    }       
}

Note: Using this method, you can have as many, or as few, cases as you need, the important thing is to keep a steady flow of calls to GetAsyncKeyState(). This does that via the call to delay_x().

Note2: Here is the segment (added to above definition) that will cause your program to exit:

    state = GetAsyncKeyState('k'); 
    state1 = GetAsyncKeyState('K'); 
    if ((0x80000000 & state) || 
        (0x80000000 & state1))
    {    
        printf("Kill Program"); 
        gRunning = FALSE;
    }