Hello,
I'm following this tutorial and I'm having some trouble setting the flags.
I have this C program that works properly (exits as soon as I press q
and CTRL+C
does not kill it).
#include <stdlib.h>
#include <ctype.h>
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
struct termios orig_termios;
void disableRawMode() {
tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
}
void enableRawMode() {
tcgetattr(STDIN_FILENO, &orig_termios);
atexit(disableRawMode);
struct termios raw = orig_termios;
raw.c_lflag &= ~(ECHO | ICANON | ISIG);
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}
int main() {
enableRawMode();
char c;
while (read(STDIN_FILENO, &c, 1) == 1 && c != 'q') {
if (iscntrl(c)) {
printf("%d\n", c);
} else {
printf("%d ('%c')\n", c, c);
}
}
return 0;
}
I'm trying to write the equivalent program in D.
source/app.d
import adapter;
import core.stdc.stdlib : atexit;
import core.stdc.stdio : printf;
import core.stdc.ctype : iscntrl;
termios orig_termios;
extern (C)
{
void disableRawMode() {
tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
}
}
void enableRawMode() {
tcgetattr(STDIN_FILENO, &orig_termios);
atexit(&disableRawMode);
termios raw = orig_termios;
// TODO flags ICANON and ISIG do not work
raw.c_lflag &= ~(ECHO | ICANON | ISIG);
tcsetattr(STDIN_FILENO, TCSANOW, &raw);
}
int main()
{
enableRawMode();
char c;
while (read(STDIN_FILENO, &c, 1) == 1 && c != 'q') {
if (iscntrl(c)) {
printf("%d\n", c);
} else {
printf("%d ('%c')\n", c, c);
}
}
return 0;
}
source/adapter/package.d
module adapter;
public import adapter.termios;
public import adapter.unistd;
source/adapter/termios.d
module adapter.termios;
import std.typecons : Typedef;
// from termios.h, https://github.com/openbsd/src/blob/master/sys/sys/termios.h
enum int ECHO = 0x00000008;
enum int ICANON = 0x00000100;
enum int ISIG = 0x00000080;
enum int IXON = 0x00000200;
enum int IEXTEN = 0x00000400;
enum int ICRNL = 0x00000100;
enum int TCSANOW = 0;
enum int TCSADRAIN = 1;
enum int TCSAFLUSH = 2;
enum int NCCS = 20;
alias tcflag_t = Typedef!uint;
alias cc_t = Typedef!ubyte;
// alias speed_t = Typedef!uint;
struct termios {
tcflag_t c_iflag; /* input flags */
tcflag_t c_oflag; /* output flags */
tcflag_t c_cflag; /* control flags */
tcflag_t c_lflag; /* local flags */
cc_t[NCCS] c_cc; /* control chars */
int c_ispeed; /* input speed */
int c_ospeed; /* output speed */
};
extern (C) int tcgetattr(int, termios *);
extern (C) int tcsetattr(int, int, const termios *);
source/adapter/unistd.d
module adapter.unistd;
import core.sys.posix.sys.types : ssize_t;
// TODO fix import
enum int STDIN_FILENO = 0;
// import core.stdc.stdio : STDIN_FILENO;
// from unistd.h
extern (C) ssize_t read(int fildes, void *buf, size_t nbytes);
When I try to run it with dub run
the only flag that appears to be set is ~(ECHO)
. It does exit when I press CTRL+C
and it does not exit as soon as I press q
, I must follow that with an ENTER
.
Is there a better way to use termios.h
inside D? Am I missing the point and there is a way to set these flags in D without using C libraries?