PDA

View Full Version : Capturing ctrl-keys in a C program



trilobite
February 13th, 2010, 01:33 AM
Hi all -

As part of getting into some simple C coding, I've done the code below which reads input from the user and takes action according to what is entered. Here's the code -



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <ctype.h>

void func1(void)
{
printf("Hey, you entered foo! \n");
}

void func2(void)
{
printf("Hey, you entered bar! \n");
}


void ctrl_g(void)
{
printf("Hey, you pressed CTRL g! \n");
}


int main(void)
{
char word[80];
char ch;

do {
puts("Enter some text :");
scanf("%s", word);

if ( !strcmp(word, "foo") ) {
func1();
}

else if (!strcmp(word, "bar") ) {
func2();
}

else if (!strcmp(word, "^G") ) {
ctrl_g();
}

else {
printf("Nope - I do not recognise that phrase.... \n");
}

printf("Try again? (y/n) : ");
scanf(" %c%*c", &ch);
}

while( toupper(ch) != 'N' );
return 0;
}



Ok, this works nicely for the strings "foo" and "bar" - it runs the functions as expected. However, when I press Ctrl-G, the program doesn't recognise that as such. It does not run the ctrl_g function, but instead prints my "Nope - I do not recognise that phrase...." message.

So, I'm wondering - how does one capture/read ctrl-keys entered by the user?

Many thanks in advance - bye for now -
- trilobite

MadCow108
February 13th, 2010, 01:54 AM
^G is just the representation in output
the actual ascii code is different
see:
http://www.scotek.demon.co.uk/ascii.html

But these codes are actually control charcters and the ctrl + X just seem to be mapped to these characters in some linux terminals.
So this may be highly unportable!
Also you will probably have to block the handling of the signals emitted by this in your program (most obvious SIGINT from ^C)

edit: did some testing, you'll have to block almost all signals, a few work without blocking on my system: ^A (=1) ^H (=8) ^T (=20) ^Y (=25)

I don't really think you can do this elegantly with standard C terminal input/output. You should resort to some library like ncurses which should be able to do that.
Or use a gui where you have no problems of the terminal using the same keys.

trilobite
February 13th, 2010, 10:40 AM
^G is just the representation in output
the actual ascii code is different
see:
http://www.scotek.demon.co.uk/ascii.html

But these codes are actually control charcters and the ctrl + X just seem to be mapped to these characters in some linux terminals.
So this may be highly unportable!
Also you will probably have to block the handling of the signals emitted by this in your program (most obvious SIGINT from ^C)

edit: did some testing, you'll have to block almost all signals, a few work without blocking on my system: ^A (=1) ^H (=8) ^T (=20) ^Y (=25)

I don't really think you can do this elegantly with standard C terminal input/output. You should resort to some library like ncurses which should be able to do that.
Or use a gui where you have no problems of the terminal using the same keys.

Hi MadCow - thanks very much for that!
Good news! While poking around with the code, I've now managed to solve this problem.

The magic snippet of code is as follows -


else if (!strcmp(word, "\007") ) {
ctrl_g();
}


So, the string "\007" equates to Ctrl-G. I even tried another one, just to confirm things. I tried "\001", guessing that that was Ctrl-A, and sure enough, it was!

So, that's great!

Now, I'm working on trying to read the arrow keypresses. They're a challenge - when I press the Up arrow, for example, it shows "^[[A" on the terminal. But I'm sure it should be possible to capture those keys as well....
- trilobite

nvteighen
February 13th, 2010, 12:52 PM
The correct way to catch POSIX signals is by using the signal() function from singal.h. Look at man 2 signal if you've got manpages-dev installed.

MadCow108
February 13th, 2010, 01:47 PM
Hi MadCow - thanks very much for that!
Good news! While poking around with the code, I've now managed to solve this problem.

The magic snippet of code is as follows -


else if (!strcmp(word, "\007") ) {
ctrl_g();
}


So, the string "\007" equates to Ctrl-G. I even tried another one, just to confirm things. I tried "\001", guessing that that was Ctrl-A, and sure enough, it was!

So, that's great!

Now, I'm working on trying to read the arrow keypresses. They're a challenge - when I press the Up arrow, for example, it shows "^[[A" on the terminal. But I'm sure it should be possible to capture those keys as well....
- trilobite

you could just do


if (input == somenumber)

where some number would be 1-26 in this case

it are just char's (which are 8 bit integers)
no need for a whole strcmp which works on char arrays.
e.g. 'X' is equal to 88. Single quotes are used to get the ascii value of printable chars


but note that this whole technique is very terminal dependent

r_s
February 13th, 2010, 01:55 PM
You need to use signal.h , as they are signals sent as interrupts.

trilobite
February 13th, 2010, 09:32 PM
You need to use signal.h , as they are signals sent as interrupts.

Thanks very much, r_s (and thanks also to nvteighan and MadCow (again!) ).

Ok, I'll check out signals.h to do this correctly - always the best way.... :)

Thanks again, bye for now -
- trilobite

wmcbrine
February 14th, 2010, 12:04 AM
Now, I'm working on trying to read the arrow keypresses. They're a challenge - when I press the Up arrow, for example, it shows "^[[A" on the terminal.Yes, the up arrow sends a sequence of three bytes -- ESC, '[', 'A'. Or, "\033[A".

But you might want to take a look at ncurses. It simplifies input, among other things. (It can be overkill, though.)