36

The Windows console interface (think cmd window) is to the user a pretty simple GUI. The level of efficiency with which it handles rendering, user input, and scrolling is however very high. The methods used to create this interface are undoubtedly quite different to those of a traditional desktop GUI.

I am interested in creating my own custom console/terminal for Windows, preferably using C# and .NET-based technologies (e.g. managed GDI+ or WPF). As a starting point, I'd be quite keen simply to recreate the standard simple Windows shell. I could then expand things and add features from there.

I'm looking for general guidance on how to go about creating such a console UI, but some specific points include:

  • What sort of rendering model should I use? A render loop? Partial updates (like WPF)? The WinForms model (not sure how this works)?

  • What sort of caching is used in the rendering model?

  • How are fonts loaded and how are they rendered? Are they standard TrueType fonts, bitmap fonts, or something else?

  • How is scrolled performed so efficiently?

  • Anything else you think might be relevant!

Any explanation of how the inbuilt Windows console UI (or even the superior Linux terminal UI) do these things - and how I could emulate them - would be ideal, in fact.

Edit: To be clear, I really want to do this completely from scratch. Based on a graphical framework like GDI+ or WPF, but no more.

Noldorin
  • 144,213
  • 56
  • 264
  • 302
  • I would say its efficient because its simple, there is a backing screenBuffer[row][col] of {character,colour} that maps to the console window surface such that character x, line y == buffer[x][y] - if a cell in the buffer changes & that part of the buffer is visible you can calculate the display character to update, if scrolling occurs its simple to re-render the text from [y]-scrollchange – Alex K. Feb 22 '12 at 18:06
  • @AlexK. Absolutely. But there are still some important fundamental problems to solve regarding rendering algorithms and technologies to use, even for such a simple UI. One wants to optimise it as far as possible. High CPU usage is just not acceptable. – Noldorin Feb 22 '12 at 21:43
  • 1
    I built one with windows forms. If you'd like I could post the code to it. – deltree Feb 28 '12 at 04:33
  • @deltree: Was it just based on GDI+ stuff and not WinForms controls though? If so, definitely. :-) – Noldorin Feb 28 '12 at 14:52
  • 1
    @Noldorin no, it uses winforms I imagine I could convert it to wpf, but it does what I need it to, so I haven't taken the time. I just thought you might like to see the logic of working the command prompt I/O – deltree Feb 28 '12 at 14:57
  • Purely out of curiosity: Why? Academic exercise or something else? – Tom W Feb 28 '12 at 21:00
  • @TomW: I always get asked this question so apparently it isn't as obvious to others as it is to me. I want to create an efficient console UI and extend the capabilities for graphical content beyond that of the Win32 console. It has many limitations, and is generally inferior to the Linux one for a start. – Noldorin Feb 28 '12 at 21:41
  • @deltree: I've written a basic one based on a TextBox myself, and I don't remember finding the prompt IO too challenging, though if you're willing to share, I'd be plenty glad to take a look. Thanks. :-) – Noldorin Feb 28 '12 at 21:42

7 Answers7

24

I once implemented a text output window from scratch - I wanted one that works like the Output window in Visual Studio. It turned out to be more complicated than I expected, and that was without any input capabilities.

Unfortunately the code is in C++ and belongs to a former employer, so I can't share it with you. But I can give you an idea of what to expect.

You need a way to store the lines of output that you can index quickly. If you're going to place a limit on the number of lines displayed it will also need to be easy to erase lines from the top. In C++ a deque<string> was perfect, I don't know what the equivalent is (if any) in C#.

You'll need handlers for the following Windows messages, in no particular order.

  • WM_LBUTTONDOWN - to start a selection. Use SetCapture to track the mouse while the button is down.
  • WM_LBUTTONUP - to end a selection.
  • WM_RBUTTONUP - translate into WM_CONTEXTMENU.
  • WM_CONTEXTMENU - to display a menu with Copy/Cut/Paste and whatever else you want.
  • WM_KEYDOWN - to respond to the 4 cursor keys, Home/End, PageUp/PageDown. Ctrl-A/Ctrl-C/Ctrl-X/Ctrl-V/Ctrl-D.
  • WM_PAINT - to paint the contents of the window.
  • WM_SIZE - to update the scrollbars when the window size changes.
  • WM_VSCROLL - to update the visible portion of the window during vertical scrolling.
  • WM_HSCROLL - to update the visible portion of the window during horizontal scrolling.
  • WM_CREATE - for initialization when the window is created.
  • WM_SETFOCUS - to create a system caret to show the current position in the window.
  • WM_KILLFOCUS - to kill the caret since only the currently focused window should show a caret.
  • WM_MOUSEMOVE - to track changes to the selection while the left mouse button is down.
  • WM_CAPTURECHANGED - to end a selection.
  • WM_TIMER - to auto-scroll when the cursor leaves the window during a selection.
  • WM_GETDLGCODE - to add DLGC_WANTARROWS so that the arrow keys will get through.
  • WM_MOUSEWHEEL - to respond to the mouse wheel for scrolling.

As lines are added to the text buffer, adjust the range of the scrollbars. The vertical scrollbar range will be the total number of lines, and the horizontal scrollbar range will be the width of the widest line.

It is best to have a single font which is monospaced - it makes the calculations a lot easier. The specific font technology doesn't matter so much. You just need to be able to track the positions of the characters.

How is scrolled performed so efficiently? You track the top and bottom lines of the window as it scrolls, and when a paint message comes you only paint the lines that are currently visible. The others are still in the buffer but they don't get touched. It's possible to blit the contents of the window as it scrolls and only paint the parts that come in from the top or bottom, but with today's processors it's a wasted effort - the window will do a full repaint so quickly that you won't notice.

Edit: Coincidentally I came across this Microsoft guide to scroll bars which should be essential reading for this task. http://msdn.microsoft.com/en-us/library/windows/desktop/bb787527.aspx

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • Thanks for the advice. There are a few points here in particular (e.g. WM messages, scrolling) which ought to be very helpful. I appreciate the suggestions from someone who has tried his hands at a similar project in the past! – Noldorin Feb 28 '12 at 21:47
  • Finally, is there any rendering/graphics technology that you recommend in particular? I suppose GDI+ will work best with WinForms and still be quite efficient? WPF has its appeal to though... It's just my naive attempt at this exact same project a while ago yielded a console UI that spiked the CPU horribly whenever keyboard input or scrolling occurred. :S – Noldorin Feb 28 '12 at 21:48
  • @Noldorin, my class used GDI which seems lower-level than what you want. I've also used GDI+ and don't think it would be hard to use, although I believe it's slower. I don't know enough about WPF to compare it, sorry. – Mark Ransom Feb 28 '12 at 21:57
  • Oh okay. I thought GDI+ was just the post-XP successor to GDI? Rewritten partially, and has better inbuilt support for a few things... *Shrug*. I don't see why WPF should be much slower really; my methodology was probably wrong formerly. Maybe I can try again with your advice! Ta. – Noldorin Feb 28 '12 at 22:08
8

If you want to create a beautiful .NET based command prompt replacement, then I suggest looking at http://poshconsole.codeplex.com/ It uses WPF for graphical elements and it implements a PowerShell script host. PowerShell is Microsoft's replacement for the venerable command prompt. PowerShell is installed by default with Windows 7, but you can download it for XP and Vista. PowerShell is highly extensible though scripting of using .NET objects directly. It can also be embedded in other programs.

WPF + PowerShell would be a great starting place for what you want to do. If you wanted you could then swap out PowerShell for your own command/scripting engine. If you were really ambitious you could take a whack at implementing your own scripting language on the DLR to act as your command interpreter.

Best of luck.

Brad Campbell
  • 2,969
  • 2
  • 23
  • 21
  • Thanks for the advice. As far as the scripting engine/language goes, I've actually got other designs for this console/shell (not just scripting). It's creating an efficient and reusable UI that I'm really interested in, so I can go on to extend it for custom purposes. PoshConsole could definitely have some good ideas though, you're right. It didn't cross my mind. It may be a little higher-level than I was hoping for, but good inspiration nonetheless. :-) – Noldorin Mar 05 '12 at 01:55
4

I had a big success with RichTextBox for output. Actually it does everything you may wish for: color customization, effective rendering and scrolling, clipboard operations etc. Input was organized by intercepting KeyPress events of the window.

ogggre
  • 2,204
  • 1
  • 23
  • 19
  • I'm rather hoping to do it from scratch. The RTB is bloated and predicates certain features I really don't want anyway. – Noldorin Feb 27 '12 at 23:08
4

Have a look at the Console open source project. It does a lot, and does it very well. It provides a unified console for whatever is character based. I run cmd, bash and python each in their own tab.

It is in C++ and it will not provide the fun of writing it yourself, though.

ixe013
  • 9,559
  • 3
  • 46
  • 77
  • Yeah, I actually use Console on my desktop from time to time. I've glanced at the source before, but it's not the most readable. I was hoping someone could answer my specific questions and/or provide a higher-level overview. :-) Thanks anyway though. – Noldorin Feb 28 '12 at 21:43
2

As a first and simple approach, I would use a TextBox in multi-line mode. Watch for the TextChanged or KeyPressed or KeyUp events and act accordingly. Later, you can create your own control by deriving one from a simple Panel control for instance. Rendering happens by overriding the OnPaint method (WinForms). No loop is required, just a call to Invalidate or Refresh.

Use a string array or a List<string> or a LinkedList<string> as line buffer or just use the text stored in the textbox.

If you create your own control with WinForms, System.Windows.Forms.TextRenderer is a good choice for rendering text. It will be used in OnPaint.

Scrolling is a tricky thing if you create your own control. System.Windows.Controls.Panel already contains scrolling support, however you still have to tell the control how to place and size the scroll bar buttons when the text changes. On the other hand, you have move your text when the user moves the scroll bars and to update the display accordingly. Performance should not be a problem for such a simple control.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • Thanks for your answer. I'm also tempted by the TextBox approach (indeed I experimented with it in the past), but I'm worried about a few things: a) it's overkill for what I need, b) it's not efficient enough, partly because of a, c) I sort of what to learn how to do it myself. The recommendation of `TextRenderer` and also `Panel` types seem like good ones, so cheers. As long as I go with WinForms over WPF that is... – Noldorin Feb 22 '12 at 23:21
  • 1
    I really agree. A console application isn't performance critical (it doesn't refresh often and it displays just a couple of lines), TextRender will give you enough. I do not think TextBox is the solution, after all you need to edit only the LAST line. I guess the most tricky part is NOT the drawing but the input management!!! – Adriano Repetti Mar 05 '12 at 09:15
  • @Adriano: Yes indeed... input management proved the hardest thing last time I had a shot at this too. TextRenderer is probably the way forward indeed... – Noldorin Mar 08 '12 at 15:26
2

If speed is paramount, then XNA is a very quick way (minimal coding) to draw your text at high speed. It can handle thousands of sprites at 100's of FPS. Using the SpriteBatch::DrawString() and SpriteFont you can get a high-speed "console" with very little coding effort. The standard "Game" class can be modified to re-draw only sub-regions of the screen on-demand. Caveats include imperfect kerning that will give the text a blurry appearance (you may be able to fix this by turning off anti-aliasing and being careful with your projections and sprite placement). If this is a problem, then perhaps Direct2D offers a better solution (or any of the already mentioned solutions).

axon
  • 1,190
  • 6
  • 16
  • 1
    Thanks for the answer. Yeah, I agree Direct2D could be a good solution here. WPF effectively uses Direct2D for a lot of rendering, but a naive implementation I tried a while ago was *terribly* inefficient. I wonder what I'm doing wrong. Especially with regards to font rendering, which I think is the key. See my specific question in the original post, but maybe I need to use bitmap fonts or something? Not sure if the Win32 console does that. – Noldorin Feb 28 '12 at 21:44
1

FastColoredTextBox project on the github has a Console example. It is:

  • Efficient and fast
  • Colored
  • Available in WPF and WinForm
  • In C# and easy to read

I think reading the code will help you a lot.

samad montazeri
  • 1,203
  • 16
  • 28