I work on a game in Ruby. It uses ncurses to draw window contents and refresh the screen. This all works fine on macOS, where I develop the stuff. But on Linux, I cannot get anything besides plain ASCII to print when interfacing with ncurses either from Ruby or plain C.
For example, I get this output:
Hello, world! M-b~U~TM-b~U~PM-b~U~WM-b~U~QM-b~U~QM-b~U~ZM-b~U~PM-b~U~]M-b~U~_M-b~T~@M-b~UM-"
Instead of what I put into the source:
Hello, world! ╔═╗║║╚═╝╟─╢
File encoding is UTF-8, env
or locale
output reports LANG
and LC_*
variables to be en_US
or en_US.UTF-8
where appropriate. The terminal can print these characters just fine, so it's not the font or terminal emulator setting.
Python 3 works fine, too. Ruby and C don't.
(Tested on a fresh Linux Mint 19 setup with libncurses5-dev
installed.)
So, Am I missing some setting or is the Python binding somewhat special and I'm out of luck?
What it looks like
It should look like this (macOS):
But does look like this instead:
Python Code
This code works just fine:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import locale
import curses
locale.setlocale(locale.LC_ALL, '')
stdscr = curses.initscr()
curses.noecho()
curses.cbreak()
stdscr.addstr(0, 0, "╔═╗║║╚═╝╟─╢") # dont even need .encode('UTF-8')
stdscr.refresh()
stdscr.getkey()
curses.endwin()
The docs for the curses module state that you have to do this since ncurses 5: https://docs.python.org/3/library/curses.html
Since version 5.4, the ncurses library decides how to interpret non-ASCII data using the nl_langinfo function. That means that you have to call locale.setlocale() in the application and encode Unicode strings using one of the system’s available encodings.
Ruby Code
# coding: utf-8
require "curses"
Curses.init_screen
Curses.start_color
Curses.stdscr.keypad(true) # enable arrow keys
Curses.cbreak # no line buffering / immediate key input
Curses.ESCDELAY = 0
Curses.curs_set(0) # Invisible cursor
Curses.noecho # Do not print keyboard input
Curses.stdscr.addstr(STYLES[:single])
Curses.stdscr.setpos(2,0)
Curses.stdscr.addstr(%Q{╔═╗║║╚═╝╟─╢})
Curses.getch
Tried with the ffi-ncurses
gem instead of curses
but to no avail. The output is the same.
C Code
I compile with
gcc -finput-charset=UTF-8 -fexec-charset=UTF-8 -pedantic -Wall -o main main.c -lncurses
Here's the code:
// Most of the code is sample code from "CURHELLO.C"
// (c) Copyright Paul Griffiths 1999
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <langinfo.h>
#include <locale.h>
#include <ncurses.h>
int main(void) {
// Here I tried to copy what Python is doing:
setlocale(LC_ALL, ""); // Also tried "C.UTF-8", "en_US.UTF-8"
nl_langinfo(CODESET);
WINDOW * mainwin;
if ( (mainwin = initscr()) == NULL ) {
fprintf(stderr, "Error initialising ncurses.\n");
exit(EXIT_FAILURE);
}
start_color();
clear();
cbreak();
noecho();
keypad(stdscr, TRUE);
mvaddstr(1, 1, "Hello, world! ╔═╗║║╚═╝╟─╢");
refresh();
sleep(3);
delwin(mainwin);
endwin();
refresh();
return EXIT_SUCCESS;
}
Edit
Got it working by linking to ncursesw
(note the trailing W
!), but it displays the special characters at double the width of text on Linux, using Ubuntu Mono, the same font I tried on OSX with iTerm.
Actually this should be several questions, but the most recent one asks how the characters could be shown as double-width.
The screenshot does not match the given example program. That only draws a few sample characters, while the screenshot shows boxes.
However, the particular line-drawing characters can be the problem, versus the locale tables (and depending on the actual program used). That's because those codes are ambiguous width (see U+2550). The standard leaves it up to the implementer whether to treat them as single-width or double-width. Not everyone agrees on the actual width.
If this were a bug report, I'd get the actual program source to test with, but suspect the problem lies in Ruby.