4

I am working on an alternate screen feature for my cross-platform terminal manipulating crate.

Situation

When I want to write something to console I can use the following code to write to the current standard output. This works on both UNIX and Windows systems:

write!(::std::fmt::io::stdout(), "Some text").

When I switch to the alternate screen, I don't want to write to the current standard output but to the alternate screen. This works differently depending on which platform you are on. The basics are the same: I need to store the handle somewhere globally so that I could put the stored output handle in alternate screen mode and write all my output, commands and actions to that stored output handle.

When I am in alternate mode, my code writes to the alternate screen and when in main screen modes my code writes to the main screen.

  • Unix

    For UNIX systems, I can use ANSI escape codes to switch to the alternate screen and back. I store the ::std::io::stdout() somewhere and all my UNIX code uses that handle for access to the terminal output. When in alternate screen mode, all the writes I do are done on the alternate screen and when on the main screen all the writes are done on the main screen.

  • Windows

    For Windows systems, I can use WinAPI to switch to the alternate screen buffer. I'll create a new screen buffer with CreateConsoleScreenBuffer then I'll use SetConsoleActiveScreenBuffer to change the active buffer. At last, I need to store the handle gotten from CreateConsoleScreenBuffer. Through this output handle, I can write output to the alternate screen buffer.

If I would not have used the above-described way and switched to alternate screen and just called this write!(::std::fmt::io::stdout(), "Some text"), I would write to the main screen instead of the alternate screen on both Windows and Unix systems because stdout() is a handle to the standard output.

The Question

The way described above works to a certain point; when I want to write to the stored handle.

For Unix I can do the following:

// (...) some logic to get the handle to the current screen.
let stored_handle: Write = ...;
write!(stored_handle, "Some text);

But for Windows I could not do this:

// (...) some logic to get the handle to the current screen for windows.
let stored_handle: HANDLE = ...;
write!(stored_handle, "Some text);

I could implement std::io::Write for the struct where I store the stdout so that for Windows I create my own logic for writing to the console with WinAPI. If I would do that I would be able to write to that struct like the following:

#[cfg(target_os = "windows")]
let storage = WindowsScreenManager::new();
#[cfg(not(target_os = "windows"))]
let storage = UnixScreenManager::new();

write!(storage, "Some text");

This is not ideal for my situation because I can not use the Rust string escape characters like \n \t my string will not contain any newlines or tabs when doing it this way. Think because WinAPI does not know these formatting options. Also I don't want to manage all the writing to the console for Windows manually on my side.

I really want to use the WinAPI HANDLE as std::io::Write so that it can be used in the write! macro, just like I do in UNIX. Just store the stdout() and write to that stdout() using the write! macro, but storing the HANDLE and writing to that.

I suppose that this should work since when calling println!() or write!(stdout()) on Windows it will write the to the standard output handle of the current process. But now I want to write to the alternate handle and not only to the default handle. Or am I wrong with this?

If the above cannot be done, how would I write to the alternate screen HANDLE without using my own implementation for writing to the Console using WinAPI?

Timon Post
  • 2,779
  • 1
  • 17
  • 32
  • Escapes like `\n` and `\t` in strings are replaced at compile time. What, exactly, is the problem you're having with them? – trent Jun 27 '18 at 19:34
  • If I use them I get some weird symbols in my console. Maybe because I encoded it to utf16 (which needs to be done) before writing it to the console using `WriteConsoleOutputCharacterA`. Plus that it adds some complexity on my side of handling all the writing to the console using WinAPI. – Timon Post Jun 27 '18 at 19:36
  • 1
    IIRC `WriteConsoleOutputCharacterA` is for ANSI strings. If you encoded the string in UTF-16 (UniCode) the correct function would be [`WriteConsoleOutputCharacterW`](https://learn.microsoft.com/en-us/windows/console/writeconsoleoutputcharacter). – zx485 Jun 27 '18 at 20:06
  • Still the same result the [code](https://play.rust-lang.org/?gist=b934c26597860b7d421f302360612a7e&version=stable&mode=debug) The backslashes are still shown as the symbols. – Timon Post Jun 27 '18 at 20:54
  • Does it do the same if you replace the body of `win32_string` with `value.encode_utf16().collect()`? I don't think `encode_wide` actually transcodes from UTF-8 to UTF-16; `OsStr` can be arbitrary bytes. – trent Jun 28 '18 at 01:52
  • @trentcl If I do it like you are saying I get text like S o m e t e x t in the console. And also the escape sequence characters are shown like weird symbols. – Timon Post Jun 28 '18 at 17:49

0 Answers0