d06b894e10dda771f11773999b18e8ee92c2d204
[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         if(!GetConsoleMode(*handle, &m))
97                 throw system_error("GetConsoleMode");
98         if(!SetConsoleMode(*handle, (m&~ENABLE_LINE_INPUT) | (l?ENABLE_LINE_INPUT:0)))
99                 throw system_error("SetConsoleMode");
100 #else
101         // XXX ICANON does more than just set line buffering, may need a bit more thought
102         termios t;
103         if(tcgetattr(*handle, &t)==-1)
104                 throw system_error("tcgetattr");
105         t.c_lflag = (t.c_lflag&~ICANON) | (l?ICANON:0);
106         if(tcsetattr(*handle, TCSADRAIN, &t)==-1)
107                 throw system_error("tcsetattr");
108 #endif
109 }
110
111 void Console::get_size(unsigned &rows, unsigned &cols)
112 {
113         check_access(M_WRITE);
114
115 #ifdef WIN32
116         // XXX Figure out how to do this
117         rows = 24;
118         cols = 80;
119 #else
120         struct winsize wsz;
121         if(ioctl(*handle, TIOCGWINSZ, &wsz)==-1)
122                 throw system_error("ioctl TIOCGWINSZ");
123         rows = wsz.ws_row;
124         cols = wsz.ws_col;
125 #endif
126 }
127
128 unsigned Console::do_write(const char *buf, unsigned len)
129 {
130         check_access(M_WRITE);
131
132         return sys_write(handle, buf, len);
133 }
134
135 unsigned Console::do_read(char *buf, unsigned len)
136 {
137         check_access(M_READ);
138
139         unsigned ret = sys_read(handle, buf, len);
140         if(ret==0)
141                 set_eof();
142
143         return ret;
144 }
145
146 Console &Console::instance(Stream s)
147 {
148         static Console in(INPUT);
149         static Console out(OUTPUT);
150         static Console err(ERROR);
151
152         switch(s)
153         {
154         case INPUT: return in;
155         case OUTPUT: return out;
156         case ERROR: return err;
157         }
158
159         throw invalid_argument("Console::instance");
160 }
161
162 Console &cin = Console::instance(Console::INPUT);
163 Console &cout = Console::instance(Console::OUTPUT);
164 Console &cerr = Console::instance(Console::ERROR);
165
166 } // namespace IO
167 } // namespace Msp