Reputation: 1
I am writing a vim-like text editor on C in Ubuntu in vim. Ubuntu is on WSL on Windows. I am working on a German keyboard. I am following a tutorial and I need to define ctrl + Q combination to stop getting the input.
#define CTRL_KEY(k) ((k) & 0x1f)
...
char editorReadKey() {
int nread;
char c;
while ((nread = read(STDIN_FILENO, &c, 1)) != 1) {
if (nread == -1 && errno != EAGAIN) die ("read");
}
return c;
}
/*** input ***/
void editorProcessKeypress() {
char c = editorReadKey();
switch (c) {
case CTRL_KEY('q'):
exit(0);
break;
case 'q':
exit(0);
break;
}
}
/*** init ***/
int main() {
enableRawMode();
while (1) {
editorProcessKeypress();
}
return 0;
}
The following code stops receiving the input and exits program when I press Tab + q or q, but Ctrl + q doesn't work.
Is hex-code for Ctrl somehow different from 0x1f in my case? Thank you for your help.
Upvotes: 0
Views: 74
Reputation: 2506
As a side note, the 0x1f
does not specifically "belong" to the Ctrl key. It just uses the fact, that when a printable character in the ASCII table, just as q=113
for example, is bit-masked with 0x1f
we would end up with the corresponding control character in the ASCII table - in this case: DC1 / Device Control 1 ((113 & 0x1f) == 17
).
That said, if you want to read this control character from stdin, you have to tell your terminal that it shall not process the control character.
In your case, Ctrl+Q is most likely already processed by the terminal. You can check by entering stty -a
. If output contains ^Q
it means that the control character is bound to certain actions.
This is what your enableRawMode()
function should do. Usually using termios header.
However, when speaking about termios.h, there is an alternative solution you might want to consider. With the API defined there, you can use the c_cc
part of the termios structure to override the VQUIT
character. This, of course, is only suitable for your solution when you really want to quit and not just stop the text input and do something else after.
P.S.: You should always read the current state of the termios structure and the beginning of your program with tcgetattr
and restore it on exit.
Update:
I have modified your code example so it does what I think you want it to do:
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <errno.h>
#define CTRL_KEY(k) ((k) & 0x1f)
char editorReadKey() {
int nread;
char c;
while ((nread = read(STDIN_FILENO, &c, 1)) != 1) {
if (nread == -1 && errno != EAGAIN) exit(1);
}
return c;
}
/*** input ***/
int editorProcessKeypress() {
char c = editorReadKey();
switch (c) {
case CTRL_KEY('q'):
return 1;
default:
// just output anything else
// this is LAZY cauz this is an example
// we should probably check if c is printable
write(STDOUT_FILENO, &c, 1);
return 0;
}
}
/*** init ***/
int main() {
struct termios backup;
tcgetattr(STDIN_FILENO, &backup);
struct termios settings = backup;
// disable processing of Ctrl+Q
settings.c_iflag &= ~(IXON);
// disable output + get chars immediately
settings.c_lflag &= ~(ECHO|ICANON);
tcsetattr(STDIN_FILENO, TCSANOW, &settings);
int quit = 0;
do {
quit = editorProcessKeypress();
} while (!quit);
tcsetattr(STDIN_FILENO, TCSANOW, &backup);
return 0;
}
Upvotes: 0