rustterminalutf-16

Changing console type to UTF-16 using Rust


I would like to set the console mode to UTF-16 in rust to print out UTF-16 (without macros). Converting the text from UTF-16 to UTF-8 doesn't work for my current use case. That works fine for the Linux version, but not the Windows one.

In C, this would be done with:

_setmode(_fileno(stdout), _O_U16TEXT)

How could this be done in Rust?

I've Google'd and tried the Rust Docs looking for UTF-16 and not finding anything related to writing to stdout.

The use case is playing video in the console. I already wrote the program in Python with C extensions here. I know it's not ascii, but it sounds better. Writing special characters such as the UTF-16 0x2588 (█) takes 3 bytes in UTF-8, and 2 bytes in UTF-16. In Windows, the performance of the program lacks because Windows has a slow terminal. A frame in UTF-16 (with certain params) is 30,000 bytes and 37,000 bytes in UTF-8. This results in a lower framerate for the video.


Edit

For anyone else wanting to do something similar, I did:

enum FILE {}

const STDOUT_FILENO: i32 = 1; // using `_fileno` would be more 'proper'
extern "cdecl" {
    fn _setmode(fd: i32, mode: i32) -> i32;
    fn fputws(ws: *const u16, stream: *mut FILE) -> i32;
    fn __acrt_iob_func(fd: i32) -> *mut FILE; // Use this to get the hand for stdout
}

and

    unsafe { _setmode(STDOUT_FILENO, _O_U8TEXT) }

I took pts as a Vec<u16> and did

fputws(pts.as_ptr(), __acrt_iob_func(STDOUT_FILENO));

Solution

  • _setmode is a Windows specific function that is part of their C Runtime Library (CRT). It is not provided by the Rust standard library. It also has no semantic equivalent on other platforms like Mac or Linux.

    Unfortunately, it seems like the windows-sys crate does not expose it either.

    Your best option might be to manually link against the Windows CRT Library (depending on your configuration, this might already happen automatically).

    You should then be able to define and use the function yourself using something like

    const _O_U16TEXT: i32 = 0x20000; 
    const STDOUT_FILENO: i32 = 1; // using `_fileno` would be more 'proper'
    extern "cdecl" {
        fn _setmode(fd: i32, mode: i32);
    }
    
    pub fn main()  {
        unsafe { _setmode(STDOUT_FILENO, _O_U16TEXT); }
    }