I am trying to get text input from the keyboard when the SDL-window is active.
This tutorial works fine when only English 7-bit ASCII characters are used. https://wiki.libsdl.org/Tutorials/TextInput
But when I try to type in letter in the extended ASCII like åäö it comes out like this in the console
The code to print it is is this:
SDL_Event sdl_Event;
while (SDL_PollEvent(&sdl_Event))
{
switch (sdl_Event.type)
{
case SDL_TEXTINPUT:
cout << sdl_Event.text.text;
break;
/*...*/
}
}
As far as I can figure SDL_Event.text.text is a array of four numbers.
wcout have the same problem
wcout << sdl_Event.text.text;
It also print åäö
When I try to convert it to char32_t I just get a number when I try to print it. (C32 is of the type char32_t)
C32 = sdl_Event.text.text[0]&0xFF;
C32 |= (sdl_Event.text.text[1]&0xFF) << 8;
C32 |= (sdl_Event.text.text[2]&0xFF) << 16;
C32 |= (sdl_Event.text.text[3]&0xFF) << 24;
wcout << C32;
I am stuck right now. Is there a clever way to map Unicode to the class string in C++?
it’s UTF-8 text (potentially multiple unicode chars).
you can convert it to UTF-32 with SDL_iconv(), code will look something like:
// could be a (static) global or class member or whatever
static SDL_iconv_t iconvDescriptor = -1;
void my_handle_sdl2_textevent(const SDL_TextInputEvent* ev)
{
const char* utf8str = ev->text;
if(utf8str[0] != '\0') {
if(iconvDescriptor == (SDL_iconv_t) -1) {
// if we'd just use "UTF-32" it would write a BOM, we don't want that,
// so we need to use the platform-endianess-specific thing
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
const char* toFormat = "UTF-32BE";
#else // little endian
const char* toFormat = "UTF-32LE";
#endif
iconvDescriptor = SDL_iconv_open(toFormat, "UTF-8");
if(iconvDescriptor == (SDL_iconv_t) -1) {
// error: SDL_iconv_open() failed!
return;
}
}
uint32_t utf32string[SDL_TEXTINPUTEVENT_TEXT_SIZE] = {0};
size_t inLeft = strlen(utf8str) + 1; // +1 for terminating '\0'
// *4 because utf-32 needs 4x as much space as char
size_t outLeft = 4 * SDL_TEXTINPUTEVENT_TEXT_SIZE;
char* out = (char*)utf32str;
size_t n = SDL_iconv(iconvDescriptor, &utf8str, &inLeft, &out, &outLeft);
if(n == (size_t) -1) {
// some error happened in SDL_iconv()
return;
}
for(size_t i = 0; i < len; ++i) {
if(utf32str[i] == 0) { // end of string
break;
}
// TODO: do something with utf32str[i], which is a UTF-32 char
}
// reset iconvDescriptor so it can be used again
SDL_iconv(iconvDescriptor, NULL, &inLeft, NULL, &outLeft);
}
}
// TODO: on shutdown maybe somewhere do sth like
// SDL_iconv_close(iconvDescriptor); iconvDescriptor = -1;
If you don’t need UTF-32 but some other encoding (unfortunately, what wchar_t and std::string are encoded like is platform specific) you could adept the above code for that encoding if SDL_iconv() supports it (it should)
But you should probably try to use UTF-8 everywhere.
Almost forgot, if you only need to convert on/for Windows (other platforms generally support UTF-8) you can also use Windows-specific conversion functions which are less painful to use. Example:
#include <windows.h>
void doSomethingWithUtf8(const char* utf8str)
{
wchar_t wcharStr[SDL_TEXTINPUTEVENT_TEXT_SIZE] = {0};
if(MultiByteToWideChar(CP_UTF8, 0, utf8str, -1, wcharStr,
SDL_TEXTINPUTEVENT_TEXT_SIZE) > 0)
{
// TODO: do something with wcharStr
}
}
It did not compile so I assumed you meant this
void my_handle_sdl2_textevent(const SDL_TextInputEvent* ev)
{
const char* utf8str = ev->text;
if (utf8str[0] != '\0')
{
if (iconvDescriptor == (SDL_iconv_t)-1)
{
// if we'd just use "UTF-32" it would write a BOM, we don't want that,
// so we need to use the platform-endianess-specific thing
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
const char* toFormat = "UTF-32BE";
#else // little endian
const char* toFormat = "UTF-32LE";
#endif
iconvDescriptor = SDL_iconv_open(toFormat, "UTF-8");
if (iconvDescriptor == (SDL_iconv_t)-1)
{
// error: SDL_iconv_open() failed!
return;
}
}
uint32_t utf32str[SDL_TEXTINPUTEVENT_TEXT_SIZE] = { 0 };
size_t inLeft = strlen(utf8str) + 1; // +1 for terminating '\0'
// *4 because utf-32 needs 4x as much space as char
size_t outLeft = 4 * SDL_TEXTINPUTEVENT_TEXT_SIZE;
char* out = (char*)utf32str;
size_t n = SDL_iconv(iconvDescriptor, &utf8str, &inLeft, &out, &outLeft);
if (n == (size_t)-1)
{
// some error happened in SDL_iconv()
return;
}
for (size_t i = 0; i < inLeft; ++i)
{
if (utf32str[i] == 0) { // end of string
break;
}
// TODO: do something with utf32str[i], which is a UTF-32 char
}
// reset iconvDescriptor so it can be used again
SDL_iconv(iconvDescriptor, NULL, &inLeft, NULL, &outLeft);
}
}
When I call it like this
my_handle_sdl2_textevent(&sdl_Event.text);
cout << iconvDescriptor << endl;
I get this exception
Exception thrown at 0x000000006C7A7AA1 (SDL2.dll) in Game.exe: 0xC0000005: Access violation reading location 0x0000000000000000. occurred
at
size_t n = SDL_iconv(iconvDescriptor, &utf8str, &inLeft, &out, &outLeft);
the iconvDescriptor does not contain the text.
utf32str
does (see the // TODO: do something with utf32str[i]
line) - but probably std::cout doesn’t support utf32 strings either.
so you’d have to adapt the code to produce UTF-16, which corresponds to wchar_t
(only on windows though, on Linux and other systems wchar_t
is UTF-32 - but then again most non-Windows systems support UTF-8 directly)
or you could just do sth like the following, using MultiByteToWideChar()
(see https://msdn.microsoft.com/en-us/library/windows/desktop/dd319072(v=vs.85).aspx):
#ifdef _WIN32
wchar_t wcharStr[SDL_TEXTINPUTEVENT_TEXT_SIZE] = {0};
if(MultiByteToWideChar(CP_UTF8, 0, sdl_Event.text.text, -1, wcharStr,
SDL_TEXTINPUTEVENT_TEXT_SIZE) > 0)
{
std::wcout << wcharStr << std::endl;
}
#else // not windows, probably sending UTF-8 text to stdout is ok
std::cout << sdl_Event.text.text << std::endl;
#endif