Я работаю над функцией альтернативного экрана для моего кроссплатформенного терминала, управляющего корзиной.
Положение
Когда я хочу что-то записать на консоль, я могу использовать следующий код для записи в текущий стандартный вывод . Это работает в системах UNIX и Windows:
write!(::std::fmt::io::stdout(), "Some text").
Когда я переключаюсь на альтернативный экран, я не хочу записывать на текущий стандартный вывод , а на альтернативный экран. Это работает по-разному в зависимости от того, на какой платформе вы находитесь. Основы те же: мне нужно хранить дескриптор где-нибудь в глобальном масштабе, чтобы я мог перевести сохраненный output handle
в альтернативный режим экрана и записать все свои выходные данные, команды и действия в этот сохраненный output handle
.
Когда я нахожусь в альтернативном режиме, мой код записывает на альтернативный экран, а в режиме основного экрана мой код записывает на главный экран.
Unix
Для систем UNIX я могу использовать ESC-коды для переключения на альтернативный экран и обратно . Я храню ::std::io::stdout()
где-нибудь, и весь мой код UNIX использует этот дескриптор для доступа к терминалу output
. В режиме альтернативного экрана все write
s, которые я делаю, выполняются на альтернативном экране, а когда на главном экране все write
s выполняются на главном экране.
Windows
Для систем Windows я могу использовать WinAPI до , чтобы переключиться на альтернативный экранный буфер . Я создам новый экранный буфер с CreateConsoleScreenBuffer
, затем я буду использовать SetConsoleActiveScreenBuffer
для изменения активного буфера. Наконец, мне нужно сохранить ручку, полученную из CreateConsoleScreenBuffer
. С помощью этого дескриптора вывода я могу записать вывод в буфер альтернативного экрана.
Если бы я не использовал описанный выше способ и не переключился на альтернативный экран, а просто назвал это write!(::std::fmt::io::stdout(), "Some text")
, я бы записал на основной экран вместо альтернативного экрана в системах Windows и Unix, поскольку stdout()
ручка для стандартного вывода .
Вопрос
Способ, описанный выше, работает до определенного момента; когда я хочу записать в сохраненный дескриптор.
Для Unix я могу сделать следующее:
// (...) some logic to get the handle to the current screen.
let stored_handle: Write = ...;
write!(stored_handle, "Some text);
Но для Windows я мог бы не сделать это:
// (...) some logic to get the handle to the current screen for windows.
let stored_handle: HANDLE = ...;
write!(stored_handle, "Some text);
Я мог бы реализовать std::io::Write
для структуры, в которой я храню stdout
, чтобы для Windows я создал свою собственную логику для записи в консоль с WinAPI. Если бы я сделал это, я мог бы написать в эту структуру, как показано ниже:
#[cfg(target_os = "windows")]
let storage = WindowsScreenManager::new();
#[cfg(not(target_os = "windows"))]
let storage = UnixScreenManager::new();
write!(storage, "Some text");
Это не идеально для моей ситуации, потому что я не могу использовать экранирующие символы строки Rust, такие как \n \t
, моя строка не будет содержать символов новой строки или табуляции при этом. Подумайте, потому что WinAPI не знает эти параметры форматирования. Также я не хочу управлять всей записью в консоли для Windows вручную на моей стороне.
Я действительно хочу использовать WinAPI HANDLE
в качестве std::io::Write
, чтобы его можно было использовать в макросе write!
, как я это делаю в UNIX. Просто сохраните stdout()
и запишите в него stdout()
, используя макрос write!
, но сохраните HANDLE
и запишите в него.
Полагаю, это должно работать, так как при вызове println!()
или write!(stdout())
в Windows он записывает в стандартный дескриптор вывода текущего процесса. Но теперь я хочу писать в альтернативный дескриптор, а не только в дескриптор по умолчанию. Или я не прав?
Если вышеперечисленное не может быть выполнено, как мне написать на альтернативном экране HANDLE
без использования моей собственной реализации для записи в консоль с использованием WinAPI?