1 using System;
  2 using System.Threading;
  3 using System.IO;
  4 using System.Collections;
  5 using System.Unicode;
  6 using cmsx.machine;
  7 
  8 namespace cmsx.kernel
  9 {
 10     public delegate void ConsoleInputCallback();
 11 
 12     public Kernel& GetKernel()
 13     {
 14         return Kernel.Instance();
 15     }
 16 
 17     public const ushort FOREGROUND_BLUE = 0x01u;
 18     public const ushort FOREGROUND_GREEN = 0x02u;
 19     public const ushort FOREGROUND_RED = 0x04u;
 20     public const ushort FOREGROUND_INTENSITY = 0x08u;
 21     public const ushort BACKGROUND_BLUE = 0x10u;
 22     public const ushort BACKGROUND_GREEN = 0x20u;
 23     public const ushort BACKGROUND_RED = 0x40u;
 24     public const ushort BACKGROUND_INTENSITY = 0x80u;
 25 
 26     public enum ConsoleColor : byte
 27     {
 28         black = 0udarkBlue = 1udarkGreen = 2udarkCyan = 3udarkRed = 4udarkYellow = 6ugray = 7u
 29         darkGray = 8ublue = 9ugreen = 10ucyan = 11ured = 12umagenta = 13uyellow = 14uwhite = 15u
 30     }
 31 
 32     public string ConsoleColorName(ConsoleColor color)
 33     {
 34         switch (color)
 35         {
 36             case ConsoleColor.black: return "black";
 37             case ConsoleColor.darkBlue: return "darkblue";
 38             case ConsoleColor.darkGreen: return "dardgreen";
 39             case ConsoleColor.darkCyan: return "dardcyan";
 40             case ConsoleColor.darkRed: return "darkred";
 41             case ConsoleColor.darkYellow: return "darkyellow";
 42             case ConsoleColor.gray: return "gray";
 43             case ConsoleColor.darkGray: return "darkgray";
 44             case ConsoleColor.blue: return "blue";
 45             case ConsoleColor.green: return "green";
 46             case ConsoleColor.cyan: return "cyan";
 47             case ConsoleColor.red: return "red";
 48             case ConsoleColor.magenta: return "magenta";
 49             case ConsoleColor.yellow: return "yellow";
 50             case ConsoleColor.white: return "white";
 51         }
 52         return string();
 53     }
 54 
 55     public const uchar beginColors = cast<uchar>(0x100000u);
 56     public const uchar endColors = cast<uchar>(0x10FFFDu);
 57 
 58     public inline nothrow bool BeginColors(uchar c)
 59     {
 60         return (cast<uint>(c) & cast<uint>(beginColors)) == cast<uint>(beginColors);
 61     }
 62 
 63     public inline nothrow bool EndColors(uchar c)
 64     {
 65         return c == endColors;
 66     }
 67 
 68     public inline nothrow void GetColors(uchar cConsoleColor& foregroundColorConsoleColor& backgroundColor)
 69     {
 70         ushort x = cast<ushort>(cast<uint>(c));
 71         foregroundColor = cast<ConsoleColor>(cast<byte>(x));
 72         backgroundColor = cast<ConsoleColor>(cast<byte>(x >> 8u));
 73     }
 74 
 75     private bool consoleDriverExited = false;
 76 
 77     public class ConsoleDriver
 78     {
 79         static ConsoleDriver() : instance(new ConsoleDriver())
 80         {
 81         }
 82         public static ConsoleDriver& Instance()
 83         {
 84             return *instance;
 85         }
 86         private ConsoleDriver() : 
 87             consoleInputHandle(OsGetStdHandle(stdin))consoleOutputHandle(OsGetStdHandle(stdout))machine(GetMachine())
 88             pos(0)cursorPosX(0)cursorPosY(0)outputEndCursorPosX(0)outputEndCursorPosY(0)attributes(0u)obeyColorStrings(false)echo(true)
 89         {
 90             ConsoleInputCallback callback = ConsoleInputWaitingCallback;
 91             ulong callBackValue = cast<ulong>(cast<void*>(callback));
 92             bool retVal = OsRegisterConsoleCallback(consoleInputHandlecallBackValue);
 93             if (!retVal)
 94             {
 95                 Panic("Console driver callback initialization failed");
 96             }
 97         }
 98         public void Stop()
 99         {
100             consoleDriverExited = true;
101             consoleInputHandle = null;
102         }
103         public void GetDimensions(int& rowsint& cols)
104         {
105             GetDimensions();
106             rows = screenSizeY;
107             cols = screenSizeX;
108         }
109         public void GetDimensions()
110         {
111             if (!OsGetConsoleScreenBufferInfo(consoleOutputHandle&cursorPosX&cursorPosY&screenSizeX&screenSizeY&attributes))
112             {
113                 Panic("Console driver screen buffer info initialization failed");
114             }
115         }
116         public inline nothrow bool Eof() const
117         {
118             return eof;
119         }
120         public inline nothrow void ResetEof()
121         {
122             eof = false;
123         }
124         public inline nothrow bool Echo() const
125         {
126             return echo;
127         }
128         public inline nothrow void SetEcho(bool echo_)
129         {
130             echo = echo_;
131         }
132         public nothrow bool HasLine() const
133         {
134             return !lines.IsEmpty();
135         }
136         public nothrow string GetLine() const
137         {
138             return ToUtf8(lines.RemoveFirst());
139         }
140         public inline nothrow void* GetConsoleInputHandle()
141         {
142             return consoleInputHandle;
143         }
144         public inline nothrow void* GetConsoleOutputHandle()
145         {
146             return consoleOutputHandle;
147         }
148         public void GetConsoleColors(ConsoleColor& foregroundColorConsoleColor& backgroundColor)
149         {
150             foregroundColor = cast<ConsoleColor>(cast<byte>(attributes & 0x0Fu));
151             backgroundColor = cast<ConsoleColor>(cast<byte>((attributes >> 8u) & 0x0Fu));
152         }
153         public void SetConsoleColors(ConsoleColor foregroundColorConsoleColor backgroundColor)
154         {
155             ushort attrs = cast<ushort>(cast<byte>(foregroundColor)) | (cast<ushort>(cast<byte>(backgroundColor)) << 8u);
156             // Note if passing OsGetStdHandle(stdout) from here as a parameter, the following call fails, but it does not fail if we call GetStdHandle() from the DLL!!!
157             if (!OsSetConsoleTextAttribute(attrs))
158             {
159                 Panic("Console driver could not set text attributes: WIN32 error " + ToString(OsGetLastError()));
160             }
161             attributes = attrs;
162         }
163         public void PushColors(ConsoleColor foregroundColorConsoleColor backgroundColor)
164         {
165             attributeStack.Push(attributes);
166             SetConsoleColors(foregroundColorbackgroundColor);
167         }
168         public void PopColors()
169         {
170             attributes = attributeStack.Pop();
171             OsSetConsoleTextAttribute(attributes);
172         }
173         public void BeginObeyColorStrings()
174         {
175             obeyColorStrings = true;
176         }
177         public void EndObeyColorStrings()
178         {
179             obeyColorStrings = false;
180         }
181         public void WriteToConsole(byte* bufferlong count)
182         {
183             ustring u;
184             for (long i = 0; i < count; ++i;)
185             {
186                 byte x = buffer[i];
187                 unicodeEngine.Put(x);
188                 if (unicodeEngine.ResultReady())
189                 {
190                     uchar c = unicodeEngine.Get();
191                     if (c != '\r')
192                     {
193                         u.Append(c);
194                     }
195                 }
196             }
197             if (!u.IsEmpty())
198             {
199                 if (obeyColorStrings)
200                 {
201                     WriteWithColors(u);
202                 }
203                 else
204                 {
205                     Write(u);
206                 }
207             }
208         }
209         private void Write(const ustring& u)
210         {
211             OsWriteConsole(consoleOutputHandleu.Chars());
212             GetDimensions();
213             outputEndCursorPosX = cursorPosX;
214             outputEndCursorPosY = cursorPosY;
215         }
216         public void WriteWithColors(const ustring& u)
217         {
218             ustring buffer;
219             for (uchar c : u)
220             {
221                 if (EndColors(c))
222                 {
223                     if (!buffer.IsEmpty())
224                     {
225                         Write(buffer);
226                         buffer.Clear();
227                     }
228                     PopColors();
229                 }
230                 else if (BeginColors(c))
231                 {
232                     if (!buffer.IsEmpty())
233                     {
234                         Write(buffer);
235                         buffer.Clear();
236                     }
237                     ConsoleColor foregroundColor;
238                     ConsoleColor backgroundColor;
239                     GetColors(cforegroundColorbackgroundColor);
240                     PushColors(foregroundColorbackgroundColor);
241                 }
242                 else
243                 {
244                     buffer.Append(c);
245                 }
246             }
247             if (!buffer.IsEmpty())
248             {
249                 Write(buffer);
250             }
251         }
252         public void KeyPressed(uchar c)
253         {
254             AddCharToInputBuffer(c);
255             machine.GetRegisters().SetInterrupt(KEYBOARD_BIT);
256         }
257         public void InterruptService()
258         {
259             if (Log())
260             {
261                 LogMessage("con.interrupt""begin");
262             }
263             GetDimensions();
264             outputEndCursorPosY = cursorPosY;
265             ustring chars = GetCharsFromInputBuffer();
266             for (uchar c : chars)
267             {
268                 switch (c)
269                 {
270                     case cast<uchar>(keyEnter): HandleEnter(); break;
271                     case cast<uchar>(keyControlD): HandleEof(); break;
272                     case cast<uchar>(keyHome): HandleHome(); break;
273                     case cast<uchar>(keyEnd): HandleEnd(); break;
274                     case cast<uchar>(keyLeft): HandleLeft(); break;
275                     case cast<uchar>(keyRight): HandleRight(); break;
276                     case cast<uchar>(keyDel): HandleDelete(); break;
277                     case cast<uchar>(keyBackspace): HandleBackspace(); break;
278                     default: HandleChar(c); break;
279                 }
280             }
281             if (Log())
282             {
283                 LogMessage("con.interrupt""end");
284             }
285         }
286         private void DeliverInputLine()
287         {
288             if (Log())
289             {
290                 LogMessage("con.deliver""begin");
291             }
292             Machine& machine = GetMachine();
293             Kernel& kernel = GetKernel();
294             Session* session = kernel.CurrentSession();
295             if (session->fgpid != -1)
296             {
297                 ProcessTable& processTable = kernel.GetProcessTable();
298                 if (Log())
299                 {
300                     LogMessage("con.deliver""fgpid=" + ToString(session->fgpid));
301                 }
302                 cmsx.kernel.Process* reader = processTable.GetProcess(session->fgpid);
303                 if (reader != null)
304                 {
305                     if (reader->state == cmsx.kernel.Process.State.asleep && reader->event == consoleInputEvent)
306                     {
307                         GlobalFileTable& fileTable = kernel.FileTable();
308                         ConsoleInputFile* consoleInputFile = fileTable.GetConsoleInputFile();
309                         if (!consoleInputFile->HasBufferedInput())
310                         {
311                             if (HasLine())
312                             {
313                                 consoleInputFile->SetBufferedInput(GetLine());
314                             }
315                         }
316                         if (Log())
317                         {
318                             LogMessage("con.deliver""wakeup");
319                         }
320                         WakeUpProcess(processTablereader);
321                         if (processTable.GetRunning() == processTable.GetIdle())
322                         {
323                             if (Log())
324                             {
325                                 LogMessage("con.interrupt""schedule");
326                             }
327                             machine.GetRegisters().SetInterrupt(CLOCK_BIT);
328                         }
329                     }
330                 }
331                 else
332                 {
333                     if (Log())
334                     {
335                         LogMessage("con.deliver""not.delivered");
336                     }
337                 }
338             }
339             if (Log())
340             {
341                 LogMessage("con.deliver""end");
342             }
343         }
344         private void HandleEnter()
345         {
346             line.Append('\n');
347             lines.Add(line);
348             line.Clear();
349             DeliverInputLine();
350             pos = 0;
351             ++cursorPosY;
352             SetCursorPos(0cursorPosY);
353             outputEndCursorPosX = 0;
354             outputEndCursorPosY = cursorPosY;
355         }
356         private void HandleEof()
357         {
358             eof = true;
359             lines.Add(line);
360             line.Clear();
361             DeliverInputLine();
362             pos = 0;
363             ++cursorPosY;
364             SetCursorPos(0cursorPosY);
365             outputEndCursorPosX = 0;
366             outputEndCursorPosY = cursorPosY;
367         }
368         private void HandleHome()
369         {
370             pos = 0;
371             if (echo)
372             {
373                 SetCursorPos(outputEndCursorPosXoutputEndCursorPosY);
374             }
375         }
376         private void HandleEnd()
377         {
378             pos = cast<int>(line.Length());
379             if (echo)
380             {
381                 SetCursorPos(outputEndCursorPosX + posoutputEndCursorPosY);
382             }
383         }
384         private void HandleLeft()
385         {
386             if (pos > 0)
387             {
388                 --pos;
389                 if (echo)
390                 {
391                     SetCursorPos(outputEndCursorPosX + posoutputEndCursorPosY);
392                 }
393             }
394         }
395         private void HandleRight()
396         {
397             if (pos < line.Length())
398             {
399                 ++pos;
400                 if (echo)
401                 {
402                     SetCursorPos(outputEndCursorPosX + posoutputEndCursorPosY);
403                 }
404             }
405         }
406         private void HandleDelete()
407         {
408             if (pos < line.Length())
409             {
410                 ++pos;
411                 line = line.Substring(0pos - 1) + line.Substring(pos);
412                 --pos;
413                 UpdateLine();
414             }
415         }
416         private void HandleBackspace()
417         {
418             if (pos > 0)
419             {
420                 line = line.Substring(0pos - 1) + line.Substring(pos);
421                 --pos;
422                 UpdateLine();
423                 if (echo)
424                 {
425                     SetCursorPos(outputEndCursorPosX + posoutputEndCursorPosY);
426                 }
427             }
428         }
429         private void HandleChar(uchar c)
430         {
431             if (IsGraphic(c) && cursorPosX < screenSizeX - 1)
432             {
433                 if (pos < line.Length())
434                 {
435                     line = line.Substring(0pos) + ustring(c1) + line.Substring(pos);
436                 }
437                 else
438                 {
439                     line.Append(c);
440                 }
441                 ++pos;
442                 UpdateLine();
443                 if (echo)
444                 {
445                     SetCursorPos(outputEndCursorPosX + posoutputEndCursorPosY);
446                 }
447             }
448         }
449         private void AddCharToInputBuffer(uchar c)
450         {
451             LockGuard<Mutex> lock(inputBufferMutex);
452             inputBuffer.Append(c);
453         }
454         private ustring GetCharsFromInputBuffer()
455         {
456             LockGuard<Mutex> lock(inputBufferMutex);
457             ustring chars;
458             Swap(charsinputBuffer);
459             return chars;
460         }
461         private void SetCursorPos(int xint y)
462         {
463             cursorPosX = x;
464             cursorPosY = y;
465             OsSetConsoleCursorPosition(consoleOutputHandlexy);
466         }
467         private void UpdateLine()
468         {
469             if (!echo) return;
470             int prevCursorPosX = cursorPosX;
471             int prevCursorPosY = cursorPosY;
472             SetCursorPos(outputEndCursorPosXoutputEndCursorPosY);
473             ustring updateLine;
474             cursorPosX = outputEndCursorPosX;
475             cursorPosY = outputEndCursorPosY;
476             for (int i = 0; i < line.Length(); ++i;)
477             {
478                 if (cursorPosX + i < screenSizeX)
479                 {
480                     updateLine.Append(line[i]);
481                 }
482                 else
483                 {
484                     OsWriteConsole(consoleOutputHandleupdateLine.Chars());
485                     updateLine.Clear();
486                     cursorPosX = 0;
487                     ++cursorPosY;
488                 }
489             }
490             if (!updateLine.IsEmpty())
491             {
492                 OsWriteConsole(consoleOutputHandleupdateLine.Chars());
493                 updateLine.Clear();
494             }
495             for (int i = cursorPosX; i < screenSizeX; ++i;)
496             {
497                 updateLine.Append(' ');
498             }
499             OsWriteConsole(consoleOutputHandleupdateLine.Chars());
500             SetCursorPos(prevCursorPosXprevCursorPosY);
501         }
502         private static UniquePtr<ConsoleDriver> instance;
503         private void* consoleInputHandle;
504         private void* consoleOutputHandle;
505         private Machine& machine;
506         private ustring inputBuffer;
507         private Mutex inputBufferMutex;
508         private ustring line;
509         private List<ustring> lines;
510         private bool eof;
511         private int pos;
512         private int cursorPosX;
513         private int cursorPosY;
514         private int outputEndCursorPosX;
515         private int outputEndCursorPosY;
516         private int screenSizeX;
517         private int screenSizeY;
518         private ushort attributes;
519         private Stack<ushort> attributeStack;
520         private UnicodeEngine unicodeEngine;
521         private bool obeyColorStrings;
522         private bool echo;
523     }
524 
525     public void ConsoleInputWaitingCallback()
526     {
527         bool exited = consoleDriverExited;
528         if (exited) return;
529         ConsoleDriver& consoleDriver = GetConsoleDriver();
530         uchar c = '\0';
531         bool retVal = OsReadConsoleInput(consoleDriver.GetConsoleInputHandle()&c);
532         if (retVal && c != '\0')
533         {
534             consoleDriver.KeyPressed(c);
535         }
536     }
537 
538     public ConsoleDriver& GetConsoleDriver()
539     {
540         return ConsoleDriver.Instance();
541     }
542 }