How should I create a custom graphical console/ter

2020-05-14 08:01发布

问题:

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.

回答1:

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



回答2:

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.



回答3:

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.



回答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.



回答5:

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).



回答6:

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.



回答7:

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.