I'm writing an app using Rust and ncurses.
I'm trying to display a box drawing character ('┃' (Unicode 9475 / 0x2503)), but it's as if it's not even printed to screen. When I select the text, it can be seen:
Here's a minimal example:
use ncurses::*;
fn populate_line_numbers() {
for i in 0..LINES() {
mvaddstr(i, 0, &i.to_string());
}
mvvline(0, 4, 9475, LINES());
refresh();
}
fn main() {
setlocale(LcCategory::all, "");
initscr();
start_color();
keypad(stdscr(), true);
noecho();
loop {
let user_input = get_wch();
match user_input.unwrap() {
WchResult::Char(ch) => {
match ch {
27 => break,
_ => {
addstr(&std::char::from_u32(ch).unwrap().to_string());
refresh();
}
}
},
WchResult::KeyCode(code) => {
match code {
KEY_F5 => {
populate_line_numbers();
},
_ => {}
}
}
}
}
endwin();
}
Hit F5 to make the program show line numbers.
How can the character be like the normal text? I tried to OR it together with a bunch of things, but nothing worked out.
Cargo.toml
[dependencies]
ncurses = { version = "5.99.0", features = ["wide"] }
Author's note: The following is a best-effort analysis based mostly on examining ncurses.h on my system and comparing it to the source code of ncurses-rs. I may be wrong in some particulars.
In ncurses, the chtype type alias (which is the third argument of mvvline) represents a single-byte character plus some appearance metadata. It cannot store a multibyte character. So mvvline simply cannot display ┃.
(Presumably the reason you get an invisible line is because the value 9475 represents a control character with some bogus appearance information. I wasn't able to figure out how to decode it. If you fiddle around with the byte values I'm sure you can get the line to change colors and contain any ASCII character you want.)
To draw a line using a "wide" (multibyte) character, you must link to a version of ncurses with wide character support, and use the wide character version of mvvline, which appears to be called mvvline_set. This function takes a const cchar_t * argument instead of chtype, where cchar_t is a struct containing a multibyte character plus some metadata.
Unfortunately, although the ncurses crate does link to the correct version of the ncurses library, it does not expose any way to call mvvline_set. There are a number of other functions that are also missing from the ncurses-rs API, mostly the ones that use the cchar_t struct. This means you won't be able to use Unicode line-drawing characters in character-oriented ncurses functions, unless you write the bindings yourself.
You should still be able to use multibyte characters in UTF-8 strings and pass them to string-oriented functions such as mvaddstr.
Related
I am trying to print Chinese character in a ncurses screen
log::debug!("row str {}", row_str);
ncurses::addstr(&row_str);
variable row_str is displayed well in as a parameter of log::debug , but get garbled by using ncurses::addstr, like this:
中彖~G潛®弾U // it should be "中文目录"
i've tried to fix it by the following 3 methods, but no one works.
// method 1
gettextrs::setlocale(gettextrs::LocaleCategory::LcAll, "");
// method 2
use libc::setlocale;
unsafe {
setlocale(0, "".as_bytes().as_ptr() as *const i8);
}
//method 3
ncurses::setlocale(ncurses::constants::LcCategory::all, "")
I am looking for a way to set where the carriage return, returns to or an equivalent way to do so.
For example I have a line like this:
^ denotes cursor location
myshell>cat file.txt
^
After carriage return it should look like this.
myshell>cat file.txt
^
You're probably after what's collectively called ANSI escape sequences. Its hard to search for if you really have no idea what you're after.
This tiny example saves/restores cursor position:
#include <stdio.h>
int main(int argc, char**argv)
{
char cmd_buf[100];
cmd_buf[0]=0;
while(strncmp(cmd_buf, "quit", 4))
{
printf("mypromt>\033[s <-Cursor should go there\033[u");
fflush(stdout);
fgets(cmd_buf, sizeof(cmd_buf), stdin);
printf("\nYou entered: %s\n", cmd_buf);
}
}
Note that in terminator, gnome-terminal and xterm on Ubuntu, this "magically" supports CTRL+U as-is, but not CTRL+A or CTRL+E.
There are many, many more sequences available. The wikipedia page is probably the simplest reference to get you started.
Update: Also, unless you're doing this as a learning exercise (which I get the impression Benjamin is), to build an interactive shell, you should probably use one of the two well established libraries for shell-style line editing, namely:
readline (GPLv3, but far more popular)
editline (BSD licensed, closest "second place")
They are the libraries that provide the emacs-style (typical default) and vi-style keybindings and history features we all know and love from bash, python, lua, perl, node, etc, etc.
For positioning on the screen, termios is of limited use (the ioctl's dealing with screensize are not in POSIX), and unless you want to assume a lot about the terminal characteristics, control characters and escape sequences have their limitations.
You can do what's asked in curses using the filter function to tell the library you want to use just the current line of the display. As written, the question is puzzling since it does not mention any output other than the current line. But for example (this is exactly what was asked):
#include <curses.h>
int
main(void)
{
int ch, y, x;
filter();
initscr();
cbreak();
addstr("myshell>");
getyx(stdscr, y, x);
while ((ch = getch()) != ERR) {
if (ch == '\n')
move(y, x);
}
endwin();
return 0;
}
However, a usable program would do more than that. There's an example of the filter() function in ncurses-examples, which you may find useful for reading. A screenshot:
I'm running into a disconnect between the online documentation and the behavior I see in my programs accessing C structs within GO code. go version says I am using:
go version go1.4.2 linux/amd64
According to the GO CGO documentation:
Within the Go file, C's struct field names that are keywords in Go can be
accessed by prefixing them with an underscore: if x points at a C struct with
a field named "type", x._type accesses the field. C struct fields that cannot
be expressed in Go, such as bit fields or misaligned data, are omitted in the
Go struct, replaced by appropriate padding to reach the next field or the end
of the struct.
I had troubles with this, so made a quick sample program to test it out:
package main
// struct rec
// {
// int i;
// double d;
// char* s;
// };
import "C"
import "fmt"
func main() {
s := "hello world"
r := C.struct_rec{}
r.i = 9
r.d = 9.876
r.s = C.CString(s)
fmt.Printf("\n\tr.i: %d\n\tr.d: %f\n\tr.s: %s\n",
r.i,
r.d,
C.GoString(r.s))
}
When I use underscores as the docs indicate (eg, substitute r._i for r.i above) I get the following compile error:
r._i undefined (type C.struct_rec has no field or method _i)
When I don't use underscores it works fine. I tried this with both pointers and non-pointers. The only other idea I can think of is that maybe it's because I allocated the instances in GO rather than C, is that the case??
Thanks for any help!
The answer is in the very quote you have in your question:
Within the Go file, C's struct field names that are keywords in Go can be accessed by prefixing them with an underscore(…)
i, d, and s are not keywords in Go.
So I already know how to convert wstring to string (How to convert wstring into string?).
However, I would like to to know whether it is safe to make the conversion, meaning, the wstring variable does not contain any characters that are not supported in string type.
strings can hold any data, if you use the right encoding. They are just sequences of bytes. But you need to check with your particular encoding / conversion routine.
Should be simply a matter of round-tripping. An elegant solution to many things.
Warning, Pseudo-code, there is no literal convert_to_wstring() unless you make it so:
if(convert_to_wstring(convert_to_string(ws)) == ws)
happy_days();
If what goes in comes out, it is non-lossy (at least for your code points).
Not that its the most efficient solution, but should allow you to build from your favorite conversion routines.
// Round-trip and see if we lose anything
bool check_ws2s(const std::wstring& wstr)
{
return (s2ws(ws2s(str)) == wstr);
}
Using #dk123's conversions for C++11 at How to convert wstring into string? (Upvote his answer here https://stackoverflow.com/a/18374698/257090)
wstring s2ws(const std::string& str)
{
typedef std::codecvt_utf8<wchar_t> convert_typeX;
std::wstring_convert<convert_typeX, wchar_t> converterX;
return converterX.from_bytes(str);
}
string ws2s(const std::wstring& wstr)
{
typedef std::codecvt_utf8<wchar_t> convert_typeX;
std::wstring_convert<convert_typeX, wchar_t> converterX;
return converterX.to_bytes(wstr);
}
Note, if your idea of conversion is truncating the wide chars to chars, then it is simply a matter of iterating and checking that each wide char value fits in a char. This will probably do it.
WARNING: Not appropriate for multibyte encoding.
for(wchar_t& wc: ws) {
if(wc > static_cast<char>::(wc))
return false;
}
return true;
Or:
// Could use a narrowing cast comparison, but this avoids any warnings
for(wchar_t& wc: ws) {
if(wc > std::numeric_limits<char>::max())
return false;
}
return true;
FWIW, in Win32, there are conversion routines that accept a parameter of WC_ERR_INVALID_CHARS that tells the routine to fail instead of silently dropping code points. Non-standard solutions, of course.
Example: WideCharToMultiByte()
http://msdn.microsoft.com/en-us/library/windows/desktop/dd374130(v=vs.85).aspx
I am use next type of strings:
LPCSTR, TCHAR, String i want to convert:
from TCHAR to LPCSTR
from String to char
I convert from TCHAR to LPCSTR by that code:
RunPath = TEXT("C:\\1");
LPCSTR Path = (LPCSTR)RunPath;
From String to char i convert by that code:
SaveFileDialog^ saveFileDialog1 = gcnew SaveFileDialog;
saveFileDialog1->Title = "Сохранение файла-настроек";
saveFileDialog1->Filter = "bck files (*.bck)|*.bck";
saveFileDialog1->RestoreDirectory = true;
pin_ptr<const wchar_t> wch = TEXT("");
if ( saveFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK ) {
wch = PtrToStringChars(saveFileDialog1->FileName);
} else return;
ofstream os(wch, ios::binary);
My problem is that when i set "Configuration Properties -> General
Character Set in "Use Multi-Byte Character Set" the first part of code work correctly. But the second part of code return error C2440. When i set "Configuration Properties -> General
Character Set in "Use Unicode" the second part of code work correctly. But the first part of code return the only first character from TCHAR to LPCSTR.
I'd suggest you need to be using Unicode the whole way through.
LPCSTR is a "Long Pointer to a C-type String". That's typically not what you want when you're dealing with .Net methods. The char type in .Net is 16bits wide.
You also should not use the TEXT("") macro unless you're planning multiple builds using various character encodings. Try wrapping all your string literals with the _W("") macro instead and a pure unicode build if you can.
See if that helps.
PS. std::wstring is very handy in your scenario.
EDIT
You see only one character because the string is now unicode but you cast it as a regular string. Many or most of the Unicode characters in the ASCII range has their same number as in ASCII but have the second of their 2 bytes set to zero. So when a unicode string is read as a C-string you only see the first character because C-strings are null ( zero ) terminated. The easy ( and wrong ) way to deal with this is to use std:wstring to cast as a std:string then pull the C-String out of that. This is not the safe approach because Unicode has a much large character space then your standard encoding.