996
|
1 // interactive.cpp
|
|
2
|
|
3 #include "lib.h"
|
|
4 #include "interactive.h"
|
|
5 #include "Thread.h"
|
|
6 #include "Board.h"
|
|
7 #include "Vec.h"
|
|
8 #include "Binding.h"
|
|
9 #include "ActivationRecord.h"
|
|
10
|
|
11 using namespace std;
|
|
12
|
|
13 // In theory, these should all be mutually exclusive.
|
|
14
|
|
15 #ifdef _WIN32
|
|
16
|
|
17 #include "windows.h"
|
|
18
|
|
19 void redisplay(Thread & program);
|
|
20 void gotoPosition(int x, int y);
|
|
21 void printString(string const & line);
|
|
22 void setColor(WORD bits);
|
|
23 char readChar(void);
|
|
24 string getLastErrorString(void);
|
|
25 string convertToDebugString(string const & line);
|
|
26
|
|
27 HANDLE inputHandle;
|
|
28 HANDLE outputHandle;
|
|
29
|
|
30 static const WORD FOREGROUND_WHITE = FOREGROUND_BLUE | FOREGROUND_GREEN
|
|
31 | FOREGROUND_RED;
|
|
32
|
|
33 static const WORD BACKGROUND_WHITE = BACKGROUND_BLUE | BACKGROUND_GREEN
|
|
34 | BACKGROUND_RED;
|
|
35
|
|
36 void runInteractive(Thread & program)
|
|
37 {
|
|
38 inputHandle = GetStdHandle(STD_INPUT_HANDLE);
|
|
39 outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
40 SetConsoleMode(inputHandle, ENABLE_PROCESSED_INPUT);
|
|
41 SetConsoleMode(outputHandle, ENABLE_PROCESSED_OUTPUT);
|
|
42 COORD bufferSize;
|
|
43 bufferSize.X = 80;
|
|
44 bufferSize.Y = 25;
|
|
45 SetConsoleScreenBufferSize(outputHandle, bufferSize);
|
|
46
|
|
47 char choice = '\0';
|
|
48 bool doRead = true;
|
|
49 while (program.getStatus() == Error::runnable)
|
|
50 {
|
|
51 redisplay(program);
|
|
52 if (doRead)
|
|
53 {
|
|
54 choice = readChar();
|
|
55 if (choice == 's')
|
|
56 {
|
|
57 program.step();
|
|
58 }
|
|
59 else if (choice == 'r')
|
|
60 {
|
|
61 doRead = false;
|
|
62 }
|
|
63 else if (choice == 'x')
|
|
64 {
|
|
65 break;
|
|
66 }
|
|
67 else
|
|
68 {
|
|
69 // Do nothing.
|
|
70 }
|
|
71 }
|
|
72 else
|
|
73 {
|
|
74 program.step();
|
|
75 }
|
|
76 }
|
|
77 }
|
|
78
|
|
79 void redisplay(Thread & program)
|
|
80 {
|
|
81 list<Binding> const & dataStack(program.getDataStack());
|
|
82 list<ActivationRecord> const & programStack(program.getProgramStack());
|
|
83 ActivationRecord const & currentRecord(programStack.front());
|
|
84 Board const * currentBoard(currentRecord.getFunction());
|
|
85 if (currentBoard == NULL)
|
|
86 {
|
|
87 throw InternalException("redisplay(): currentBoard is NULL");
|
|
88 }
|
|
89 Vec currentPos = currentRecord.getPosition();
|
|
90 // We want the current position in the center. Therefore we subtract
|
|
91 // this offset from all position calculations in the program world.
|
|
92 static const int windowOffset = 11;
|
|
93 int y = 0;
|
|
94 string line;
|
|
95 line.reserve(35);
|
|
96
|
|
97 // Print window on the board.
|
|
98 setColor(FOREGROUND_WHITE);
|
|
99 for (y = 0; y < 23; ++y)
|
|
100 {
|
|
101 line.clear();
|
|
102 for (int x = 0; x < 23; ++ x)
|
|
103 {
|
|
104 line.push_back(currentBoard->at(Vec(x + currentPos.x - windowOffset,
|
|
105 y + currentPos.y - windowOffset)));
|
|
106 }
|
|
107 gotoPosition(0, y);
|
|
108 printString(line);
|
|
109 }
|
|
110
|
|
111 // Print current position in a different color for emphasis.
|
|
112 gotoPosition(11,11);
|
|
113 setColor(FOREGROUND_WHITE | BACKGROUND_BLUE);
|
|
114 line.clear();
|
|
115 line.push_back(currentBoard->at(currentPos));
|
|
116 printString(line);
|
|
117
|
|
118 // Print program stack
|
|
119 setColor(FOREGROUND_WHITE);
|
|
120 gotoPosition(24, 0);
|
|
121 printString("Program Stack (top)");
|
|
122 gotoPosition(24, 1);
|
|
123 printString("-------------------");
|
|
124 list<ActivationRecord>::const_iterator programPos = programStack.begin();
|
|
125 list<ActivationRecord>::const_iterator programLimit = programStack.end();
|
|
126 if (programStack.size() >= 21)
|
|
127 {
|
|
128 y = 2;
|
|
129 }
|
|
130 else
|
|
131 {
|
|
132 y = 23 - static_cast<int>(programStack.size());
|
|
133 }
|
|
134 // 1
|
|
135 // 0123456789012345678
|
|
136 static const string programBlank = " ";
|
|
137 int blankY = 0;
|
|
138 for (blankY = 2; blankY < y; ++blankY)
|
|
139 {
|
|
140 gotoPosition(24, blankY);
|
|
141 printString(programBlank);
|
|
142 }
|
|
143 for (; programPos != programLimit && y < 23; ++programPos, ++y)
|
|
144 {
|
|
145 gotoPosition(24, y);
|
|
146 line = programPos->getFunction()->getName();
|
|
147 if (line.size() > 19)
|
|
148 {
|
|
149 line.resize(19);
|
|
150 }
|
|
151 while (line.size() < 19)
|
|
152 {
|
|
153 line.push_back(' ');
|
|
154 }
|
|
155 printString(line);
|
|
156 }
|
|
157
|
|
158 // Print data stack
|
|
159 if (dataStack.size() >= 21)
|
|
160 {
|
|
161 y = 2;
|
|
162 }
|
|
163 else
|
|
164 {
|
|
165 y = 23 - static_cast<int>(dataStack.size());
|
|
166 }
|
|
167 static const string dataBlank =
|
|
168 // 1 2 3 4
|
|
169 // 012345678901234567890123456789012345678901234
|
|
170 " ";
|
|
171 for (blankY = 0; blankY < y; ++blankY)
|
|
172 {
|
|
173 gotoPosition(44, blankY);
|
|
174 printString(dataBlank);
|
|
175 }
|
|
176 gotoPosition(44, 0);
|
|
177 printString("Data Stack (top)");
|
|
178 gotoPosition(44, 1);
|
|
179 printString("----------------");
|
|
180 list<Binding>::const_iterator dataPos = dataStack.begin();
|
|
181 list<Binding>::const_iterator dataLimit = dataStack.end();
|
|
182 for (; dataPos != dataLimit && y < 23; ++dataPos, ++y)
|
|
183 {
|
|
184 gotoPosition(44, y);
|
|
185 line = convertToDebugString((*dataPos)->toString());
|
|
186 if (line.size() > 36)
|
|
187 {
|
|
188 line.resize(36);
|
|
189 }
|
|
190 while (line.size() < 36)
|
|
191 {
|
|
192 line.push_back(' ');
|
|
193 }
|
|
194 printString(line);
|
|
195 }
|
|
196
|
|
197 // Print blank columns
|
|
198 setColor(FOREGROUND_WHITE | BACKGROUND_GREEN);
|
|
199 static const string blankCol = " ";
|
|
200 for (y = 0; y < 23; ++y)
|
|
201 {
|
|
202 gotoPosition(23, y);
|
|
203 printString(blankCol);
|
|
204 gotoPosition(43, y);
|
|
205 printString(blankCol);
|
|
206 }
|
|
207 for (int x = 0; x < 80; ++x)
|
|
208 {
|
|
209 gotoPosition(x, 23);
|
|
210 printString(blankCol);
|
|
211 }
|
|
212 gotoPosition(0, 23);
|
|
213 printString("(" + intToString(currentPos.x) + ", "
|
|
214 + intToString(currentPos.y) + ") "
|
|
215 + Dir::dirToString(currentRecord.getDirection()));
|
|
216
|
|
217 setColor(FOREGROUND_WHITE | BACKGROUND_RED);
|
|
218 for (int x = 0; x < 80; ++x)
|
|
219 {
|
|
220 gotoPosition(x, 24);
|
|
221 printString(blankCol);
|
|
222 }
|
|
223 gotoPosition(0, 24);
|
|
224 printString("Please (s)tep, (r)un, or e(x)it");
|
|
225 setColor(FOREGROUND_WHITE);
|
|
226 }
|
|
227
|
|
228 void gotoPosition(int x, int y)
|
|
229 {
|
|
230 COORD pos;
|
|
231 pos.X = x;
|
|
232 pos.Y = y;
|
|
233 if (SetConsoleCursorPosition(outputHandle, pos) == 0)
|
|
234 {
|
|
235 throw InternalException("redisplay(): Failed SetConsoleCursorPosition ("
|
|
236 + intToString(pos.X) + ", " + intToString(pos.Y)
|
|
237 + "): " + getLastErrorString());
|
|
238 }
|
|
239 }
|
|
240
|
|
241 void printString(string const & line)
|
|
242 {
|
|
243 DWORD writeCount = 0;
|
|
244 BOOL error = WriteConsole(outputHandle, line.c_str(), static_cast<DWORD>(line.size()),
|
|
245 &writeCount, NULL);
|
|
246 if (error == 0)
|
|
247 {
|
|
248 throw InternalException("printString(): Failed WriteConsole (" + line
|
|
249 + "): " + getLastErrorString());
|
|
250 }
|
|
251 if (writeCount < line.size())
|
|
252 {
|
|
253 throw InternalException(
|
|
254 "printString(): WriteConsole didn't write enough characters (" + line
|
|
255 + ")");
|
|
256 }
|
|
257 }
|
|
258
|
|
259 void setColor(WORD bits)
|
|
260 {
|
|
261 if (SetConsoleTextAttribute(outputHandle, bits) == 0)
|
|
262 {
|
|
263 throw InternalException("setColor(): Failed SetConsoleTextAttribute(): "
|
|
264 + getLastErrorString());
|
|
265 }
|
|
266 }
|
|
267
|
|
268 char readChar(void)
|
|
269 {
|
|
270 char buffer[2];
|
|
271 DWORD readCount = 0;
|
|
272 if (ReadConsole(inputHandle, buffer, 1, &readCount, NULL) == 0)
|
|
273 {
|
|
274 throw InternalException("readChar(): Failed ReadConsole(): "
|
|
275 + getLastErrorString());
|
|
276 }
|
|
277 if (readCount < 1)
|
|
278 {
|
|
279 throw InternalException("readChar(): ReadConsole didn't read any characters.");
|
|
280 }
|
|
281 return buffer[0];
|
|
282 }
|
|
283
|
|
284 string getLastErrorString(void)
|
|
285 {
|
|
286 char * buffer = NULL;
|
|
287 DWORD errorNumber = GetLastError();
|
|
288 string error = "Error #" + intToString(errorNumber) + ": ";
|
|
289 if (!FormatMessage(
|
|
290 FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
291 FORMAT_MESSAGE_FROM_SYSTEM |
|
|
292 FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
293 NULL,
|
|
294 errorNumber,
|
|
295 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
|
296 (LPTSTR) &buffer,
|
|
297 0,
|
|
298 NULL ))
|
|
299 {
|
|
300 error += "META ERROR! An error occurred while trying to get the text for GetLastError()";
|
|
301 }
|
|
302 else
|
|
303 {
|
|
304 error += buffer;
|
|
305 }
|
|
306 LocalFree(buffer);
|
|
307 return error;
|
|
308 }
|
|
309
|
|
310 string convertToDebugString(string const & line)
|
|
311 {
|
|
312 string result;
|
|
313 result.reserve(line.size());
|
|
314 for (size_t i = 0; i < line.size(); ++i)
|
|
315 {
|
|
316 if (line[i] == '\\')
|
|
317 {
|
|
318 result += "\\\\";
|
|
319 }
|
|
320 else if (line[i] == '\n')
|
|
321 {
|
|
322 result += "\\n\\";
|
|
323 }
|
|
324 else if (line[i] == '\t')
|
|
325 {
|
|
326 result += "\\t\\";
|
|
327 }
|
|
328 else if (line[i] == '[')
|
|
329 {
|
|
330 result += "\\[\\";
|
|
331 }
|
|
332 else if (line[i] == ']')
|
|
333 {
|
|
334 result += "\\]\\";
|
|
335 }
|
|
336 else
|
|
337 {
|
|
338 result += line[i];
|
|
339 }
|
|
340 }
|
|
341 return result;
|
|
342 }
|
|
343
|
|
344 #endif
|
|
345
|
|
346 #ifdef __GNUC__
|
|
347
|
|
348 void runInteractive(Thread & program)
|
|
349 {
|
|
350 throw InternalException("Interactive mode is not yet implemented for *nix.");
|
|
351 }
|
|
352
|
|
353 #endif
|