unix C++ solution to getch() question

This post asks about the equivalent of getch() from <conio.h>.
http://www.cplusplus.com/forum/unices/264588/#msg1140069

As was mentioned in that thread (now archived), there's no portable way to do it that will work for windows and linux/unix. But it can be done for any recent linux. This might work on mac's too, depending on the availability of cfmakeraw

The underlying problem is that C++ doesn't know anything about the underlying OS. If we want to change the way the OS feeds us input, we have to tell it. Thus there's no portable way to do this.

file rawterm.h:
1
2
3
4
5
6
7
8
9
#include <termios.h>

class rawTerm
{
    termios initState; // the intial state of the terminal
public:
    rawTerm();
    ~rawTerm();
};


file rawterm.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <fcntl.h>
#include <termios.h>

#include "rawterm.h"


rawTerm:: rawTerm() {
    tcgetattr(0, &initState); // get term attrributes on fd 0 (cin/stdin)
    termios workios = initState;  // make a copy to work with;
    cfmakeraw(&workios);      // set flags to put term in 'raw' mode
    tcsetattr(0, TCSANOW, &workios);  // tell the OS to put change the term attributes
}

rawTerm::~rawTerm() {
    // put the term back into 'cooked mode
    tcsetattr(0, TCSANOW, &initState);
}



example of use:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <cctype>
#include "rawterm.h"

void getinput()
{
    rawTerm rt;
    char ch;
    while( (ch = std::cin.get()) != 'q' ) {
        std::cout << static_cast<char>(toupper(ch)) << std::flush;
    }
}

int main()
{
    getinput();
    std::cout << std::endl;
}


cfmakeraw() sets the terminal into raw mode: no echo, no line buffering, and turns off processing of control chars.
Last edited on
Actually Some macros are supplied I’m pretty sure...

You can put the function def in a .h file with the .cpp have a bunch of preprocessor checks.

I remember I found this somewhere, I forget exactly how, but it should work maybe take it for a spin.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

#ifndef PORTABLE_H
#define PORTABLE_H


#if defined(unix) || defined(__unix__) || defined(__unix)
#define typeCheck

// Unix code
    
#endif
  
#ifndef typeCheck 
#ifdef __linux__
    
 // Linux code
    
#endif
#endif
  
#ifndef typeCheck
#ifdef _WIN32
#define typeCheck
    
// windows code
    
#endif
#endif

#endif 
I think you can do a getch with just a couple of lines of assembly, and its gonna be non portable anyway, may as well make it that way without all the hoops.
You need to test if the input stream has anything to read. You can do that with select().
Registered users can post here. Sign in or register to post.