Search code examples
cunicodencurses

Handling Unicode characters in C and NCURSES


I'm trying to display some unicode characters in a C program. A working MWE can be seen below:

#include <ncurses.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <locale.h>


int main(int argc, char *argv[]) 
{ 
    setlocale(LC_ALL, "");
    initscr();              // Initialize stdscr

    for(int x = 0; x < 20; x++)
    {
        switch (x%5)
        {
            case 0:
                mvaddstr(1, x, "\u2588");
                break;
            case 1:
                mvaddstr(1, x, "\u2593");
                break;
            case 2:
                mvaddstr(1, x, "\u2592");
                break;
            case 3:
                mvaddstr(1, x, "\u2591");
                break;
            case 4:
                mvaddstr(1, x, " ");
                break;
        }
    }

    mvprintw(3, 0, "Press ANY KEY to finish");
    refresh();
    int ch = getch();
    endwin();

    return 0;
}

To compile use gcc -o shades shades.c -lncursesw. It compiles fine and shows the shades correctly, as we can see in image below.

shades

But instead using a case/switch statement I would like put my characters into an array of hexadecimal codes and iterate over it. As the shameful attempt below.

#include <ncurses.h>
#include <stdlib.h>
#include <stdio.h>
#include <locale.h>

int main(int argc, char *argv[]) 
{ 
    setlocale(LC_ALL, "");
    initscr();              // Initialize stdscr

    uint shades[5] = { 0x2588,
                       0x2593,
                       0x2592,
                       0x2591,
                       ' '};

    char utfchar[7];

    for(int x = 0; x < 20; x++)
    {
        sprintf(utfchar, "\\u%04x", shades[x%5]);
        mvaddstr(1, x, utfchar);
    }

    mvprintw(3, 0, "Press ANY KEY to finish");
    refresh();

    int ch = getch();
    endwin();

    return 0;
}

Here I'm using sprintf to convert the hexadecimal value into a string formatted as \u0000 where 0000 are the correct hexadecimal value. Then I use mvaddstr as I did in previous code, since mvaddstr expects a const char * in third argument.

This is one of the many failed attemps. I'm not being able to copy the strings correctly in unicode format, neither being able to use a variable as argument to mvaddstr when I try to add unicode content.

I would like to know how can I format the unicode capable const char * from a uint valid unicode hex value to insert it into the mvaddstr?

PS: I'm not using C++ just plain C in Linux. C++ solutions are not a solution


Solution

  • You could simply put the strings in your array:

    const char *shades[] = { "\u2588",
                             "\u2593",
                             "\u2592",
                             "\u2591",
                             " "};
    
    for(int x = 0; x < 20; x++)
    {
        mvaddstr(1, x, shades[x%4]);
    }
    

    If you want to do it with codepoints, you need to encode it as UTF8 (or anything NCurse expects):

    void sprintutf8(char *buffer, uint32_t code)
    {
        if (code < 0x80)
            sprintf(buffer, "%c", code);
        else if (code < 0x800)
            sprintf(buffer, "%c%c",
                0xC0 | (code >> 6),
                0x80 | (code & 0x3F));
        else
            sprintf(buffer, "%c%c%c",
                0xE0 | (code >> 12),
                0x80 | (code >> 6 & 0x3F),
                0x80 | (code & 0x3F));
    }
    
    [...]
    
    for(int x = 0; x < 20; x++)
    {
        sprintutf8(utfchar, shades[x%4]);
        mvaddstr(1, x, utfchar);
    }