Обработка нажатий клавиш без ввода?

Я не знаю точно, как сформулировать то, что я пытаюсь спросить; в С++, используя заголовок stdio.h вместо iostream, как мне сделать так, чтобы при нажатии клавиши escape в любой момент программа завершалась? Есть ли что-то, что я мог бы добавить один раз вверху программы, или мне нужно было бы добавить это в каждый цикл/условие отдельно? Ниже приведен мой код (функция sleep() предназначена только для визуальной загрузки/вычисления):

#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;
}

person Mat Jones    schedule 24.02.2015    source источник
comment
Не лучше ли использовать Ctrl+C вместо esc?   -  person ForceBru    schedule 24.02.2015
comment
Проверьте значение, возвращаемое scanf, и выйдите, если оно меньше ожидаемого.   -  person Austin Mullins    schedule 24.02.2015
comment
@AustinMullins: scanf сделает паузу для ввода.   -  person musiphil    schedule 24.02.2015
comment
@musiphil Хороший вопрос, но если пользователь хочет выйти, он может просто нажать Enter без ввода, верно?   -  person Austin Mullins    schedule 24.02.2015
comment
возможный дубликат захвата нажатия клавиши в C++   -  person musiphil    schedule 24.02.2015
comment
Вы смотрели GetAsyncKeyState() ?   -  person ryyker    schedule 24.02.2015
comment
@AustinMullins: Да. Но я предполагаю, что ОП хотел, чтобы программа продолжалась без перерыва, если не нажата клавиша Esc.   -  person musiphil    schedule 24.02.2015
comment
К вашему сведению, всю функцию repeat здесь можно заменить рекурсивным вызовом quadratic, или, что еще лучше, бросить все это в цикл while(1) or for(;;). Если ваш компилятор не может преобразовать хвостовую рекурсию в цикл, это в конечном итоге взорвет стек вызовов.   -  person Brian McFarland    schedule 24.02.2015
comment
GetAsyncKeyState() делает именно это. Запустите его в цикле while или в отдельном потоке и используйте его вместе с switch() для обработки множества различных сценариев ответа ключ/ответ. один простой пример здесь   -  person ryyker    schedule 24.02.2015
comment
Как мне заставить GetAsyncKeyState() работать непрерывно, не прерывая остальную часть моего кода?   -  person Mat Jones    schedule 24.02.2015
comment
Как заставить GetAsyncKeyState() работать непрерывно...? Смотрите изменения в моем ответе ниже.   -  person ryyker    schedule 25.02.2015


Ответы (2)


Терминал, используемый в stdio, скорее всего, будет буферизован (приготовлен). Если это так, проверка клавиши escape через scanf не сработает.

СМОТРЕТЬ ЭТИ URL-адреса:

Захват символов из стандартного ввода без ожидания введите для нажатия

сканирование не считывает ввод

CURSES или NCURSES обнаружат управляющую клавишу (символ ASCII 27), в зависимости от типа терминала.

Этот код можно использовать в WINDOWS для проверки клавиши ESCAPE.

    #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 );
person A B    schedule 24.02.2015

Можно ли добавить что-то однократно вверху программы, или мне придется добавлять это в каждый цикл/условие по отдельности?

Я думаю, что вы можете добавить что-то один раз и использовать это, чтобы легко перехватывать события нажатия клавиш во всей вашей программе.

Ниже приведен фрагмент кода, показывающий функцию, которую я использовал для обработки событий нажатия клавиш в консольном приложении. Он использует GetAsyncKeyState(). Включен раздел, в котором показано, как захватить клавишу CTRL и как вы можете что-то сделать, когда увидите ее. (приведенный фрагмент показывает, как я фиксирую последовательность клавиш <ctrl><shift><h>, чтобы отобразить меню справки для использования этой конкретной процедуры.

Примечание. В описании delay_x(float delay) – это просто настраиваемая неблокирующая функция сна или задержки, которая включает вызов следующего фрагмента. Он вызывается из основного цикла программы while(1){...}. Выход из программы предусмотрен одной из комбинаций клавиш: <CTRL><SHIFT><K>

Фрагмент кода:

/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
//
//  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  

Правка — ответьте на вопросы в комментариях как вызвать GetAsyncKeyState()

Есть много способов, которыми вы могли бы вызвать GetAsyncKeyState() в то время, когда происходят другие действия. Нитки — хороший способ. Вы также можете сделать все это в строке, используя комбинацию while()/switch(){}. Вот очень простой пример того, как вы могли бы это сделать (в псевдокоде)

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.
    }       
}

Примечание. Используя этот метод, вы можете иметь столько cases, сколько вам нужно. Важно поддерживать постоянный поток вызовов GetAsyncKeyState(). Это делается через вызов delay_x().

Примечание 2. Вот сегмент (добавленный к приведенному выше определению), который приведет к завершению работы вашей программы:

    state = GetAsyncKeyState('k'); 
    state1 = GetAsyncKeyState('K'); 
    if ((0x80000000 & state) || 
        (0x80000000 & state1))
    {    
        printf("Kill Program"); 
        gRunning = FALSE;
    }
person ryyker    schedule 24.02.2015
comment
Так что мне просто поместить такой код в начало моего кода? Также я немного запутался в том, как правильно использовать синтаксис/какие параметры функции? - person Mat Jones; 24.02.2015
comment
Google GetAsyncKeyState MSDN или щелкните ссылку, которую я оставил выше в разделе комментариев к вашему вопросу. - person ryyker; 24.02.2015
comment
Итак, мне просто поставить такой код вверху моего кода? Да. Вы определяете функцию, содержащую синтаксис, аналогичный тому, что я предоставил для примера, но для ваших собственных целей и вызовите его из цикла, работающего, возможно, из вашей функции main(). - person ryyker; 24.02.2015