447377b32ebe08d057c5e1f121601ffde893dd64
[libs/core.git] / source / io / console.cpp
1 #ifndef WIN32
2 #include <errno.h>
3 #include <fcntl.h>
4 #include <termios.h>
5 #include <sys/ioctl.h>
6 #endif
7 #include <msp/core/systemerror.h>
8 #include "console.h"
9 #include "handle_private.h"
10
11 using namespace std;
12
13 namespace {
14
15 #ifdef WIN32
16 DWORD stream_to_sys(Console::Stream stream)
17 {
18         switch(stream)
19         {
20         case INPUT: return STD_INPUT_HANDLE;
21         case OUTPUT: return STD_OUTPUT_HANDLE;
22         case ERROR: return STD_ERROR_HANDLE;
23         default: throw invalid_argument("stream_to_sys");
24         }
25 }
26
27 #else
28 termios orig_attr;
29 #endif
30
31 } // namespace
32
33 namespace Msp {
34 namespace IO {
35
36 Console::Console(Stream s):
37         stream(s)
38 {
39         mode = (stream==INPUT ? M_READ : M_WRITE);
40
41 #ifdef WIN32
42         *handle = GetStdHandle(stream_to_sys(stream));
43 #else
44         *handle = stream;
45
46         if(stream==INPUT)
47                 tcgetattr(*handle, &orig_attr);
48 #endif
49
50         if(stream==INPUT)
51                 set_events(P_INPUT);
52 }
53
54 Console::~Console()
55 {
56 #ifndef WIN32
57         if(stream==INPUT)
58                 tcsetattr(*handle, TCSADRAIN, &orig_attr);
59 #endif
60 }
61
62 void Console::set_block(bool b)
63 {
64 #ifdef WIN32
65         // XXX Dunno how to do this in win32
66         (void)b;
67 #else
68         int flags = fcntl(*handle, F_GETFL);
69         flags = (flags&~O_NONBLOCK) | (b?0:O_NONBLOCK);
70         fcntl(*handle, F_SETFL, flags);
71 #endif
72 }
73
74 void Console::set_local_echo(bool e)
75 {
76         check_access(M_READ);
77
78 #ifdef WIN32
79         DWORD m;
80         GetConsoleMode(*handle, &m);
81         SetConsoleMode(*handle, (m&~ENABLE_ECHO_INPUT) | (e?ENABLE_ECHO_INPUT:0));
82 #else
83         termios t;
84         tcgetattr(*handle, &t);
85         t.c_lflag = (t.c_lflag&~ECHO) | (e?ECHO:0);
86         tcsetattr(*handle, TCSADRAIN, &t);
87 #endif
88 }
89
90 void Console::set_line_buffer(bool l)
91 {
92         check_access(M_READ);
93
94 #ifdef WIN32
95         DWORD m;
96         GetConsoleMode(*handle, &m);
97         SetConsoleMode(*handle, (m&~ENABLE_LINE_INPUT) | (l?ENABLE_LINE_INPUT:0));
98 #else
99         // XXX ICANON does more than just set line buffering, may need a bit more thought
100         termios t;
101         tcgetattr(*handle, &t);
102         t.c_lflag = (t.c_lflag&~ICANON) | (l?ICANON:0);
103         tcsetattr(*handle, TCSADRAIN, &t);
104 #endif
105 }
106
107 void Console::get_size(unsigned &rows, unsigned &cols)
108 {
109         check_access(M_WRITE);
110
111 #ifdef WIN32
112         // XXX Figure out how to do this
113         rows = 24;
114         cols = 80;
115 #else
116         struct winsize wsz;
117         ioctl(*handle, TIOCGWINSZ, &wsz);
118         rows = wsz.ws_row;
119         cols = wsz.ws_col;
120 #endif
121 }
122
123 unsigned Console::do_write(const char *buf, unsigned len)
124 {
125         check_access(M_WRITE);
126
127         return sys_write(handle, buf, len);
128 }
129
130 unsigned Console::do_read(char *buf, unsigned len)
131 {
132         check_access(M_READ);
133
134         unsigned ret = sys_read(handle, buf, len);
135         if(ret==0)
136                 set_eof();
137
138         return ret;
139 }
140
141 Console &Console::instance(Stream s)
142 {
143         static Console in(INPUT);
144         static Console out(OUTPUT);
145         static Console err(ERROR);
146
147         switch(s)
148         {
149         case INPUT: return in;
150         case OUTPUT: return out;
151         case ERROR: return err;
152         }
153
154         throw invalid_argument("Console::instance");
155 }
156
157 Console &cin = Console::instance(Console::INPUT);
158 Console &cout = Console::instance(Console::OUTPUT);
159 Console &cerr = Console::instance(Console::ERROR);
160
161 } // namespace IO
162 } // namespace Msp