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 terminaloutput
. When in alternate screen mode, all thewrite
s I do are done on the alternate screen and when on the main screen all thewrite
s 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 useSetConsoleActiveScreenBuffer
to change the active buffer. At last, I need to store the handle gotten fromCreateConsoleScreenBuffer
. 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?