diff -U 3 -b -B -d -r -- a/cfg.h b/cfg.h --- a/cfg.h 2013-10-29 20:24:37.034116805 +0100 +++ b/cfg.h 2013-10-29 20:24:04.297449549 +0100 @@ -15,9 +15,6 @@ #include #include -#define INPUT_MAXLENGTH_NAME 30 -#define INPUT_MAXLENGTH_PASSWD 50 - #define CFGFILE SYSCONFDIR"/slim.conf" #define THEMESDIR PKGDATADIR"/themes" #define THEMESFILE "/slim.theme" @@ -29,7 +26,7 @@ ~Cfg(); bool readConf(std::string configfile); - std::string parseOption(std::string line, std::string option); + static std::string parseOption(std::string line, std::string option); const std::string& getError() const; std::string& getOption(std::string option); int getIntOption(std::string option); diff -U 3 -b -B -d -r -- a/const.h b/const.h --- a/const.h 2013-10-29 20:24:37.044116805 +0100 +++ b/const.h 2013-10-29 20:24:04.570782888 +0100 @@ -24,9 +24,6 @@ #define HIDE 0 #define SHOW 1 -#define GET_NAME 0 -#define GET_PASSWD 1 - #define OK_EXIT 0 #define ERR_EXIT 1 @@ -45,4 +42,14 @@ /* max height/width for images */ #define MAX_DIMENSION 10000 +#define INPUT_MAXLENGTH_NAME 30 +#define INPUT_MAXLENGTH_PASSWD 50 + +// Define the right charset to use +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define CHARSET_UTF_16 "UTF-16LE" +#else +#define CHARSET_UTF_16 "UTF-16BE" +#endif + #endif /* _CONST_H_ */ Binary files a/libslim.so and b/libslim.so differ Binary files a/libslim.so.1.3.6 and b/libslim.so.1.3.6 differ diff -U 3 -b -B -d -r -- a/main.cpp b/main.cpp --- a/main.cpp 2013-10-29 20:24:37.047450139 +0100 +++ b/main.cpp 2013-10-29 20:24:04.570782888 +0100 @@ -16,6 +16,8 @@ int main(int argc, char** argv) { + // We need to set the locale to use the iconv conversion function + setlocale (LC_ALL, ""); LoginApp = new App(argc, argv); LoginApp->Run(); return 0; diff -U 3 -b -B -d -r -- a/panel.cpp b/panel.cpp --- a/panel.cpp 2013-10-29 20:24:37.047450139 +0100 +++ b/panel.cpp 2013-10-29 20:24:04.607449555 +0100 @@ -13,6 +13,7 @@ #include #include #include "panel.h" +#include "util.h" using namespace std; @@ -78,11 +79,47 @@ XftColorAllocName(Dpy, visual, colormap, cfg->getOption("session_shadow_color").c_str(), &sessionshadowcolor); +#ifdef X_HAVE_UTF8_STRING + // Build XIC and XIM to be able to get unicode string from keyboard events + char classname = 0; + displayIm = XOpenIM(Dpy, NULL, &classname, &classname); + if(displayIm) { + displayIc = XCreateIC(displayIm, + XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNResourceName, &classname, + XNResourceClass, &classname, + NULL); + } + else { + displayIc = NULL; + } +#endif + + // Allocate descriptor for character set conversion, from and to UTF-16 + iconvLocaleToUtf16 = iconv_open(CHARSET_UTF_16, ""); + iconvUtf16ToLocale = iconv_open("", CHARSET_UTF_16); + /* Load properties from config / theme */ input_name_x = cfg->getIntOption("input_name_x"); input_name_y = cfg->getIntOption("input_name_y"); input_pass_x = cfg->getIntOption("input_pass_x"); input_pass_y = cfg->getIntOption("input_pass_y"); + + input_maxlength_name = cfg->getIntOption("input_maxlength_name"); + if(input_maxlength_name <= 0) { + input_maxlength_name = INPUT_MAXLENGTH_NAME; + } + nameBuffer = (uint16_t*) calloc (input_maxlength_name, sizeof(uint16_t)); + nameBufferLen = 0; + + input_maxlength_passwd = cfg->getIntOption("input_maxlength_passwd"); + if(input_maxlength_passwd <= 0) { + input_maxlength_passwd = INPUT_MAXLENGTH_PASSWD; + } + passwdBuffer = (uint16_t*) calloc (input_maxlength_passwd, sizeof(uint16_t)); + hiddenPasswdBuffer = (uint16_t*) calloc (input_maxlength_passwd, sizeof(uint16_t)); + passwdBufferLen = 0; + inputShadowXOffset = cfg->getIntOption("input_shadow_xoffset"); inputShadowYOffset = cfg->getIntOption("input_shadow_yoffset"); @@ -210,6 +247,21 @@ Visual* visual = DefaultVisual(Dpy, Scr); Colormap colormap = DefaultColormap(Dpy, Scr); +#ifdef X_HAVE_UTF8_STRING + if(displayIc) { + XDestroyIC(displayIc); + } + if(displayIm) { + XCloseIM(displayIm); + } +#endif + if(iconvLocaleToUtf16 != (iconv_t)(-1)) { + iconv_close(iconvLocaleToUtf16); + } + if(iconvUtf16ToLocale != (iconv_t)(-1)) { + iconv_close(iconvUtf16ToLocale); + } + XftColorFree(Dpy, visual, colormap, &inputcolor); XftColorFree(Dpy, visual, colormap, &inputshadowcolor); XftColorFree(Dpy, visual, colormap, &welcomecolor); @@ -229,6 +281,10 @@ XftFontClose(Dpy, welcomefont); XftFontClose(Dpy, enterfont); + free(nameBuffer); + free(passwdBuffer); + free(hiddenPasswdBuffer); + if (mode == Mode_Lock) XFreeGC(Dpy, WinGC); @@ -279,6 +335,8 @@ void Panel::WrongPassword(int timeout) { string message; XGlyphInfo extents; + int textLen; + uint16_t *textBuf; #if 0 if (CapsLockOn) @@ -289,8 +347,11 @@ XftDraw *draw = XftDrawCreate(Dpy, Win, DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr)); - XftTextExtents8(Dpy, msgfont, reinterpret_cast(message.c_str()), - message.length(), &extents); + + textLen = message.length(); + textBuf = (uint16_t*)calloc(textLen, sizeof(uint16_t)); + textLen = Util::localeStringToUtf16(iconvLocaleToUtf16, message, textBuf, textLen); + XftTextExtents16(Dpy, msgfont, (XftChar16*)textBuf, textLen, &extents); string cfgX = cfg->getOption("passwd_feedback_x"); string cfgY = cfg->getOption("passwd_feedback_y"); @@ -300,8 +361,8 @@ int msg_y = Cfg::absolutepos(cfgY, XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)), extents.height); OnExpose(); - SlimDrawString8(draw, &msgcolor, msgfont, msg_x, msg_y, message, - &msgshadowcolor, shadowXOffset, shadowYOffset); + SlimDrawString16(draw, &msgcolor, msgfont, msg_x, msg_y, + textBuf, textLen, &msgshadowcolor, shadowXOffset, shadowYOffset); if (cfg->getOption("bell") == "1") XBell(Dpy, 100); @@ -312,16 +373,19 @@ OnExpose(); // The message should stay on the screen even after the password field is // cleared, methinks. I don't like this solution, but it works. - SlimDrawString8(draw, &msgcolor, msgfont, msg_x, msg_y, message, - &msgshadowcolor, shadowXOffset, shadowYOffset); + SlimDrawString16(draw, &msgcolor, msgfont, msg_x, msg_y, + textBuf, textLen, &msgshadowcolor, shadowXOffset, shadowYOffset); XSync(Dpy, True); XftDrawDestroy(draw); + free(textBuf); } void Panel::Message(const string& text) { string cfgX, cfgY; XGlyphInfo extents; XftDraw *draw; + int textLen; + uint16_t *textBuf; if (mode == Mode_Lock) draw = XftDrawCreate(Dpy, Win, @@ -330,9 +394,11 @@ draw = XftDrawCreate(Dpy, Root, DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr)); - XftTextExtents8(Dpy, msgfont, - reinterpret_cast(text.c_str()), - text.length(), &extents); + textLen = text.length(); + textBuf = (uint16_t*)calloc(textLen, sizeof(uint16_t)); + textLen = Util::localeStringToUtf16(iconvLocaleToUtf16, text, textBuf, textLen); + XftTextExtents16(Dpy, msgfont, (XftChar16*)textBuf, textLen, &extents); + cfgX = cfg->getOption("msg_x"); cfgY = cfg->getOption("msg_y"); int shadowXOffset = cfg->getIntOption("msg_shadow_xoffset"); @@ -347,12 +413,11 @@ msg_y = Cfg::absolutepos(cfgY, XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)), extents.height); } - SlimDrawString8 (draw, &msgcolor, msgfont, msg_x, msg_y, - text, - &msgshadowcolor, - shadowXOffset, shadowYOffset); + SlimDrawString16(draw, &msgcolor, msgfont, msg_x, msg_y, + textBuf, textLen, &msgshadowcolor, shadowXOffset, shadowYOffset); XFlush(Dpy); XftDrawDestroy(draw); + free(textBuf); } void Panel::Error(const string& text) { @@ -383,24 +448,27 @@ } void Panel::Cursor(int visible) { - const char* text = NULL; - int xx = 0, yy = 0, y2 = 0, cheight = 0; + const uint16_t* text = NULL; + int xx = 0, yy = 0, y2 = 0, cheight = 0, textLen = 0; const char* txth = "Wj"; /* used to get cursor height */ if (mode == Mode_Lock) { - text = HiddenPasswdBuffer.c_str(); + text = hiddenPasswdBuffer; + textLen = passwdBufferLen; xx = input_pass_x; yy = input_pass_y; } else { switch(field) { case Get_Passwd: - text = HiddenPasswdBuffer.c_str(); + text = hiddenPasswdBuffer; + textLen = passwdBufferLen; xx = input_pass_x; yy = input_pass_y; break; case Get_Name: - text = NameBuffer.c_str(); + text = nameBuffer; + textLen = nameBufferLen; xx = input_name_x; yy = input_name_y; break; @@ -408,10 +476,10 @@ } XGlyphInfo extents; - XftTextExtents8(Dpy, font, (XftChar8*)txth, strlen(txth), &extents); + XftTextExtents8(Dpy, font, (XftChar8*)txth, 2, &extents); cheight = extents.height; y2 = yy - extents.y + extents.height; - XftTextExtents8(Dpy, font, (XftChar8*)text, strlen(text), &extents); + XftTextExtents16(Dpy, font, (XftChar16*)text, textLen, &extents); xx += extents.width; if(visible == SHOW) { @@ -478,27 +546,25 @@ XClearWindow(Dpy, Win); if (input_pass_x != input_name_x || input_pass_y != input_name_y){ - SlimDrawString8 (draw, &inputcolor, font, input_name_x, input_name_y, - NameBuffer, - &inputshadowcolor, + SlimDrawString16(draw, &inputcolor, font, input_name_x, input_name_y, + nameBuffer, nameBufferLen, &inputshadowcolor, inputShadowXOffset, inputShadowYOffset); - SlimDrawString8 (draw, &inputcolor, font, input_pass_x, input_pass_y, - HiddenPasswdBuffer, - &inputshadowcolor, + SlimDrawString16(draw, &inputcolor, font, input_pass_x, input_pass_y, + hiddenPasswdBuffer, passwdBufferLen, &inputshadowcolor, inputShadowXOffset, inputShadowYOffset); } else { /*single input mode */ switch(field) { case Get_Passwd: - SlimDrawString8 (draw, &inputcolor, font, + SlimDrawString16 (draw, &inputcolor, font, input_pass_x, input_pass_y, - HiddenPasswdBuffer, + hiddenPasswdBuffer, passwdBufferLen, &inputshadowcolor, inputShadowXOffset, inputShadowYOffset); break; case Get_Name: - SlimDrawString8 (draw, &inputcolor, font, + SlimDrawString16 (draw, &inputcolor, font, input_name_x, input_name_y, - NameBuffer, + nameBuffer, nameBufferLen, &inputshadowcolor, inputShadowXOffset, inputShadowYOffset); break; @@ -510,35 +576,46 @@ ShowText(); } -void Panel::EraseLastChar(string &formerString) { - switch(field) { - case GET_NAME: - if (! NameBuffer.empty()) { - formerString=NameBuffer; - NameBuffer.erase(--NameBuffer.end()); - } - break; - - case GET_PASSWD: - if (!PasswdBuffer.empty()) { - formerString=HiddenPasswdBuffer; - PasswdBuffer.erase(--PasswdBuffer.end()); - HiddenPasswdBuffer.erase(--HiddenPasswdBuffer.end()); - } - break; - } +/* Check if the input character is allowed */ +bool Panel::isUtf16CharAllowed(uint16_t c) { + return ((0x020 <= c && c <= 0x07E) || (0x0A0 <= c && c != 0x0AD)); } +#define SIZE_BUFFER_KEY_PRESS 64 + bool Panel::OnKeyPress(XEvent& event) { - char ascii; + int formerTextBufferLen = -1; + int textBufferLen = -1; + uint16_t *textBuffer = NULL; KeySym keysym; + int nbReadBuf = -1; + uint16_t utf16buf[SIZE_BUFFER_KEY_PRESS]; + +#ifdef X_HAVE_UTF8_STRING + if(displayIc) + { + Status status; + char databuf[SIZE_BUFFER_KEY_PRESS]; + nbReadBuf = Xutf8LookupString(displayIc, &event.xkey, databuf, + SIZE_BUFFER_KEY_PRESS, &keysym, &status); + if(nbReadBuf > 0) { + nbReadBuf = Util::utf8ToUtf16((unsigned char*)databuf, nbReadBuf, + utf16buf, SIZE_BUFFER_KEY_PRESS); + } + } + else +#endif + { XComposeStatus compstatus; - int xx = 0; - int yy = 0; - string text; - string formerString = ""; + char databuf[SIZE_BUFFER_KEY_PRESS]; + nbReadBuf = XLookupString(&event.xkey, databuf, + SIZE_BUFFER_KEY_PRESS, &keysym, &compstatus); + if(nbReadBuf > 0) { + nbReadBuf = Util::localeStringToUtf16(iconvLocaleToUtf16, databuf, + nbReadBuf, utf16buf, SIZE_BUFFER_KEY_PRESS); + } + } - XLookupString(&event.xkey, &ascii, 1, &keysym, &compstatus); switch(keysym){ case XK_F1: SwitchSession(); @@ -553,17 +630,17 @@ case XK_KP_Enter: if (field==Get_Name){ /* Don't allow an empty username */ - if (NameBuffer.empty()) return true; + if (nameBufferLen <= 0) return true; - if (NameBuffer==CONSOLE_STR){ + if (Util::utf16EqualToAscii(CONSOLE_STR, nameBuffer, nameBufferLen)) { action = Console; - } else if (NameBuffer==HALT_STR){ + } else if (Util::utf16EqualToAscii(HALT_STR, nameBuffer, nameBufferLen)) { action = Halt; - } else if (NameBuffer==REBOOT_STR){ + } else if (Util::utf16EqualToAscii(REBOOT_STR, nameBuffer, nameBufferLen)) { action = Reboot; - } else if (NameBuffer==SUSPEND_STR){ + } else if (Util::utf16EqualToAscii(SUSPEND_STR, nameBuffer, nameBufferLen)) { action = Suspend; - } else if (NameBuffer==EXIT_STR){ + } else if (Util::utf16EqualToAscii(EXIT_STR, nameBuffer, nameBufferLen)) { action = Exit; } else{ if (mode == Mode_DM) @@ -581,7 +658,25 @@ switch(keysym){ case XK_Delete: case XK_BackSpace: - EraseLastChar(formerString); + switch(field) { + case Get_Name: + if (nameBufferLen > 0) { + formerTextBufferLen = nameBufferLen; + nameBufferLen--; + textBuffer = nameBuffer; + textBufferLen = nameBufferLen; + }; + break; + case Get_Passwd: + if (passwdBufferLen > 0) { + formerTextBufferLen = passwdBufferLen; + passwdBufferLen--; + passwdBuffer[passwdBufferLen] = 0; + textBuffer = hiddenPasswdBuffer; + textBufferLen = passwdBufferLen; + }; + break; + } break; case XK_w: @@ -589,72 +684,104 @@ if (reinterpret_cast(event).state & ControlMask) { switch(field) { case Get_Passwd: - formerString = HiddenPasswdBuffer; - HiddenPasswdBuffer.clear(); - PasswdBuffer.clear(); + formerTextBufferLen = passwdBufferLen; + passwdBufferLen = 0; + textBuffer = hiddenPasswdBuffer; + textBufferLen = 0; break; case Get_Name: - formerString = NameBuffer; - NameBuffer.clear(); + formerTextBufferLen = nameBufferLen; + nameBufferLen = 0; + textBuffer = nameBuffer; + textBufferLen = 0; break; } break; } case XK_h: if (reinterpret_cast(event).state & ControlMask) { - EraseLastChar(formerString); + switch(field) { + case Get_Name: + if (nameBufferLen > 0) { + formerTextBufferLen = nameBufferLen; + nameBufferLen--; + textBuffer = nameBuffer; + textBufferLen = nameBufferLen; + }; + break; + case Get_Passwd: + if (passwdBufferLen > 0) { + formerTextBufferLen = passwdBufferLen; + passwdBufferLen--; + passwdBuffer[passwdBufferLen] = 0; + textBuffer = hiddenPasswdBuffer; + textBufferLen = passwdBufferLen; + }; + break; + } break; } /* Deliberate fall-through */ default: - if (isprint(ascii) && (keysym < XK_Shift_L || keysym > XK_Hyper_R)){ + if(nbReadBuf > 0) { switch(field) { - case GET_NAME: - formerString=NameBuffer; - if (NameBuffer.length() < INPUT_MAXLENGTH_NAME-1){ - NameBuffer.append(&ascii,1); - }; + case Get_Name: + formerTextBufferLen = nameBufferLen; + for(int i = 0; i < nbReadBuf && + nameBufferLen < input_maxlength_name; i++) { + + if(isUtf16CharAllowed(utf16buf[i])) { + nameBuffer[nameBufferLen] = utf16buf[i]; + nameBufferLen++; + } + } + textBuffer = nameBuffer; + textBufferLen = nameBufferLen; break; - case GET_PASSWD: - formerString=HiddenPasswdBuffer; - if (PasswdBuffer.length() < INPUT_MAXLENGTH_PASSWD-1){ - PasswdBuffer.append(&ascii,1); - HiddenPasswdBuffer.append("*"); - }; + + case Get_Passwd: + formerTextBufferLen = passwdBufferLen; + for(int i = 0; i < nbReadBuf && + passwdBufferLen < input_maxlength_passwd; i++) { + + if(isUtf16CharAllowed(utf16buf[i])) { + passwdBuffer[passwdBufferLen] = utf16buf[i]; + hiddenPasswdBuffer[passwdBufferLen] = (uint16_t)'*'; + passwdBufferLen++; + } + } + textBuffer = hiddenPasswdBuffer; + textBufferLen = passwdBufferLen; break; - }; - }; + } + } break; }; - XGlyphInfo extents; - XftDraw *draw = XftDrawCreate(Dpy, Win, - DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr)); + int xx = 0, yy = 0; + if (formerTextBufferLen > 0 || textBufferLen > 0) { switch(field) { case Get_Name: - text = NameBuffer; xx = input_name_x; yy = input_name_y; break; case Get_Passwd: - text = HiddenPasswdBuffer; xx = input_pass_x; yy = input_pass_y; break; } + } - if (!formerString.empty()){ + if (formerTextBufferLen > 0) { + XGlyphInfo extents; const char* txth = "Wj"; /* get proper maximum height ? */ - XftTextExtents8(Dpy, font, - reinterpret_cast(txth), strlen(txth), &extents); + XftTextExtents8(Dpy, font, (XftChar8*)txth, 2, &extents); int maxHeight = extents.height; - XftTextExtents8(Dpy, font, - reinterpret_cast(formerString.c_str()), - formerString.length(), &extents); + XftTextExtents16(Dpy, font, (XftChar16*)textBuffer, formerTextBufferLen, &extents); int maxLength = extents.width; if (mode == Mode_Lock) @@ -666,14 +793,15 @@ maxLength + 6, maxHeight + 6, false); } - if (!text.empty()) { - SlimDrawString8 (draw, &inputcolor, font, xx, yy, - text, - &inputshadowcolor, - inputShadowXOffset, inputShadowYOffset); + if(textBufferLen > 0) { + XftDraw *draw = XftDrawCreate(Dpy, Win, DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr)); + if(draw != NULL) { + SlimDrawString16(draw, &inputcolor, font, xx, yy, textBuffer, textBufferLen, + &inputshadowcolor, inputShadowXOffset, inputShadowYOffset); + XftDrawDestroy(draw); + } } - XftDrawDestroy (draw); Cursor(SHOW); return true; } @@ -690,8 +818,12 @@ XftDraw *draw = XftDrawCreate(Dpy, Win, DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr)); /* welcome message */ - XftTextExtents8(Dpy, welcomefont, (XftChar8*)welcome_message.c_str(), - strlen(welcome_message.c_str()), &extents); + int textLen = welcome_message.length(); + uint16_t *textBuf = (uint16_t*)calloc(textLen, sizeof(uint16_t)); + textLen = Util::localeStringToUtf16(iconvLocaleToUtf16, welcome_message, textBuf, textLen); + + XftTextExtents16(Dpy, welcomefont, (XftChar16*)textBuf, textLen, &extents); + cfgX = cfg->getOption("welcome_x"); cfgY = cfg->getOption("welcome_y"); int shadowXOffset = cfg->getIntOption("welcome_shadow_xoffset"); @@ -700,18 +832,21 @@ welcome_x = Cfg::absolutepos(cfgX, image->Width(), extents.width); welcome_y = Cfg::absolutepos(cfgY, image->Height(), extents.height); if (welcome_x >= 0 && welcome_y >= 0) { - SlimDrawString8 (draw, &welcomecolor, welcomefont, - welcome_x, welcome_y, - welcome_message, + SlimDrawString16(draw, &welcomecolor, welcomefont, + welcome_x, welcome_y, textBuf, textLen, &welcomeshadowcolor, shadowXOffset, shadowYOffset); } + free(textBuf); /* Enter username-password message */ string msg; if ((!singleInputMode|| field == Get_Passwd) && mode == Mode_DM) { msg = cfg->getOption("password_msg"); - XftTextExtents8(Dpy, enterfont, (XftChar8*)msg.c_str(), - strlen(msg.c_str()), &extents); + textLen = msg.length(); + textBuf = (uint16_t*)calloc(textLen, sizeof(uint16_t)); + textLen = Util::localeStringToUtf16(iconvLocaleToUtf16, msg, textBuf, textLen); + + XftTextExtents16(Dpy, enterfont, (XftChar16*)textBuf, textLen, &extents); cfgX = cfg->getOption("password_x"); cfgY = cfg->getOption("password_y"); int shadowXOffset = cfg->getIntOption("username_shadow_xoffset"); @@ -719,15 +854,20 @@ password_x = Cfg::absolutepos(cfgX, image->Width(), extents.width); password_y = Cfg::absolutepos(cfgY, image->Height(), extents.height); if (password_x >= 0 && password_y >= 0){ - SlimDrawString8 (draw, &entercolor, enterfont, password_x, password_y, - msg, &entershadowcolor, shadowXOffset, shadowYOffset); + SlimDrawString16(draw, &entercolor, enterfont, password_x, password_y, + textBuf, textLen, &entershadowcolor, + shadowXOffset, shadowYOffset); } + free(textBuf); } if (!singleInputMode|| field == Get_Name) { msg = cfg->getOption("username_msg"); - XftTextExtents8(Dpy, enterfont, (XftChar8*)msg.c_str(), - strlen(msg.c_str()), &extents); + textLen = msg.length(); + textBuf = (uint16_t*)calloc(textLen, sizeof(uint16_t)); + textLen = Util::localeStringToUtf16(iconvLocaleToUtf16, msg, textBuf, textLen); + + XftTextExtents16(Dpy, enterfont, (XftChar16*)textBuf, textLen, &extents); cfgX = cfg->getOption("username_x"); cfgY = cfg->getOption("username_y"); int shadowXOffset = cfg->getIntOption("username_shadow_xoffset"); @@ -735,9 +875,11 @@ username_x = Cfg::absolutepos(cfgX, image->Width(), extents.width); username_y = Cfg::absolutepos(cfgY, image->Height(), extents.height); if (username_x >= 0 && username_y >= 0){ - SlimDrawString8 (draw, &entercolor, enterfont, username_x, username_y, - msg, &entershadowcolor, shadowXOffset, shadowYOffset); + SlimDrawString16(draw, &entercolor, enterfont, username_x, username_y, + textBuf, textLen, &entershadowcolor, + shadowXOffset, shadowYOffset); } + free(textBuf); } XftDrawDestroy(draw); @@ -771,13 +913,15 @@ XClearWindow(Dpy, Root); string currsession = cfg->getOption("session_msg") + " " + session_name; XGlyphInfo extents; - sessionfont = XftFontOpenName(Dpy, Scr, cfg->getOption("session_font").c_str()); + int textLen = currsession.length(); + uint16_t *textBuf = (uint16_t*)calloc(textLen, sizeof(uint16_t)); + textLen = Util::localeStringToUtf16(iconvLocaleToUtf16, currsession, textBuf, textLen); + XftDraw *draw = XftDrawCreate(Dpy, Root, DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr)); - XftTextExtents8(Dpy, sessionfont, reinterpret_cast(currsession.c_str()), - currsession.length(), &extents); + XftTextExtents16(Dpy, sessionfont, (XftChar16*)textBuf, textLen, &extents); msg_x = cfg->getOption("session_x"); msg_y = cfg->getOption("session_y"); int x = Cfg::absolutepos(msg_x, XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), extents.width); @@ -785,19 +929,18 @@ int shadowXOffset = cfg->getIntOption("session_shadow_xoffset"); int shadowYOffset = cfg->getIntOption("session_shadow_yoffset"); - SlimDrawString8(draw, &sessioncolor, sessionfont, x, y, - currsession, - &sessionshadowcolor, + SlimDrawString16(draw, &sessioncolor, sessionfont, x, y, + textBuf, textLen, &sessionshadowcolor, shadowXOffset, shadowYOffset); XFlush(Dpy); XftDrawDestroy(draw); + free(textBuf); } -void Panel::SlimDrawString8(XftDraw *d, XftColor *color, XftFont *font, - int x, int y, const string& str, - XftColor* shadowColor, - int xOffset, int yOffset) +void Panel::SlimDrawString16(XftDraw *d, XftColor *color, XftFont *font, + int x, int y, uint16_t *str, int strLen, + XftColor* shadowColor, int xOffset, int yOffset) { int calc_x = 0; int calc_y = 0; @@ -807,18 +950,16 @@ } if (xOffset && yOffset) { - XftDrawStringUtf8(d, shadowColor, font, + XftDrawString16(d, shadowColor, font, x + xOffset + calc_x, y + yOffset + calc_y, - reinterpret_cast(str.c_str()), - str.length()); + (XftChar16*)str, strLen); } - XftDrawStringUtf8(d, color, font, + XftDrawString16(d, color, font, x + calc_x, y + calc_y, - reinterpret_cast(str.c_str()), - str.length()); + (XftChar16*)str, strLen); } Panel::ActionType Panel::getAction(void) const{ @@ -831,28 +972,28 @@ } void Panel::ResetName(void){ - NameBuffer.clear(); + nameBufferLen = 0; } void Panel::ResetPasswd(void){ - PasswdBuffer.clear(); - HiddenPasswdBuffer.clear(); + passwdBufferLen = 0; + memset(passwdBuffer, 0, input_maxlength_passwd * sizeof(uint16_t)); } void Panel::SetName(const string& name){ - NameBuffer=name; + nameBufferLen = Util::localeStringToUtf16(iconvLocaleToUtf16, name, nameBuffer, input_maxlength_name); if (mode == Mode_DM) action = Login; else action = Lock; } -const string& Panel::GetName(void) const{ - return NameBuffer; +const string Panel::GetName(void) const{ + return Util::utf16StringToLocale(iconvUtf16ToLocale, nameBuffer, nameBufferLen); } -const string& Panel::GetPasswd(void) const{ - return PasswdBuffer; +const string Panel::GetPasswd(void) const{ + return Util::utf16StringToLocale(iconvUtf16ToLocale, passwdBuffer, passwdBufferLen); } Rectangle Panel::GetPrimaryViewport() { diff -U 3 -b -B -d -r -- a/panel.h b/panel.h --- a/panel.h 2013-10-29 20:24:37.024116804 +0100 +++ b/panel.h 2013-10-29 20:24:04.174116213 +0100 @@ -20,9 +20,11 @@ #include #include #include +#include #include #include #include +#include #ifdef NEEDS_BASENAME #include @@ -86,23 +88,22 @@ void ResetName(void); void ResetPasswd(void); void SetName(const std::string &name); - const std::string& GetName(void) const; - const std::string& GetPasswd(void) const; + const std::string GetName(void) const; + const std::string GetPasswd(void) const; void SwitchSession(); private: Panel(); void Cursor(int visible); unsigned long GetColor(const char *colorname); void OnExpose(void); - void EraseLastChar(string &formerString); bool OnKeyPress(XEvent& event); void ShowText(); void ShowSession(); - void SlimDrawString8(XftDraw *d, XftColor *color, XftFont *font, - int x, int y, const std::string &str, - XftColor *shadowColor, - int xOffset, int yOffset); + static bool isUtf16CharAllowed(uint16_t c); + void SlimDrawString16(XftDraw *d, XftColor *color, XftFont *font, + int x, int y, uint16_t *str, int strLen, + XftColor* shadowColor, int xOffset, int yOffset); Rectangle GetPrimaryViewport(); void ApplyBackground(Rectangle = Rectangle()); @@ -136,12 +137,20 @@ XftColor entershadowcolor; ActionType action; FieldType field; +#ifdef X_HAVE_UTF8_STRING + XIM displayIm; + XIC displayIc; +#endif + iconv_t iconvLocaleToUtf16; + iconv_t iconvUtf16ToLocale; //Pixmap background; /* Username/Password */ - std::string NameBuffer; - std::string PasswdBuffer; - std::string HiddenPasswdBuffer; + uint16_t *nameBuffer; + int nameBufferLen; + uint16_t *passwdBuffer; + int passwdBufferLen; + uint16_t *hiddenPasswdBuffer; /* screen stuff */ Rectangle viewport; @@ -149,6 +158,8 @@ /* Configuration */ int input_name_x; int input_name_y; + int input_maxlength_passwd; + int input_maxlength_name; int input_pass_x; int input_pass_y; int inputShadowXOffset; Binary files a/slim and b/slim differ Binary files a/slimlock and b/slimlock differ diff -U 3 -b -B -d -r -- a/switchuser.h b/switchuser.h --- a/switchuser.h 2013-10-29 20:24:37.047450139 +0100 +++ b/switchuser.h 2013-10-29 20:24:04.570782888 +0100 @@ -32,8 +32,6 @@ void Login(const char* cmd, const char* mcookie); private: - SwitchUser(); - void SetEnvironment(); void SetUserId(); void Execute(const char* cmd); void SetClientAuth(const char* mcookie); diff -U 3 -b -B -d -r -- a/util.cpp b/util.cpp --- a/util.cpp 2013-10-29 20:24:37.047450139 +0100 +++ b/util.cpp 2013-10-29 20:24:04.570782888 +0100 @@ -67,3 +67,174 @@ return pid + tm + (ts.tv_sec ^ ts.tv_nsec); } + +/* Given a UTF-8 encoded string pointed to by utf8 of length length in +bytes, returns the corresponding UTF-16 encoded string in the +buffer pointed to by utf16. The maximum number of UTF-16 encoding +units (i.e., Unit16s) allowed in the buffer is specified in +utf16_max_length. The return value is the number of UTF-16 +encoding units placed in the output buffer pointed to by utf16. + +In case of an error, -1 is returned, leaving some unusable partial +results in the output buffer. + +The caller must estimate the size of utf16 buffer by itself before +calling this function. Insufficient output buffer is considered as +an error, and once an error occured, this function doesn't give any +clue how large the result will be. + +The error cases include following: + +- Invalid byte sequences were in the input UTF-8 bytes. The caller + has no way to know what point in the input buffer was the + errornous byte. + +- The input contained a character (a valid UTF-8 byte sequence) + whose scalar value exceeded the range that UTF-16 can represent + (i.e., characters whose Unicode scalar value above 0x110000). + +- The output buffer has no enough space to hold entire utf16 data. + +Please note: + +- '\0'-termination is not assumed both on the input UTF-8 string + and on the output UTF-16 string; any legal zero byte in the input + UTF-8 string will be converted to a 16-bit zero in output. As a + side effect, the last UTF-16 encoding unit stored in the output + buffer will have a non-zero value if the input UTF-8 was not + '\0'-terminated. + +- UTF-8 aliases are *not* considered as an error. They are + converted to UTF-16. For example, 0xC0 0xA0, 0xE0 0x80 0xA0, + and 0xF0 0x80 0x80 0xA0 are all mapped to a single UTF-16 + encoding unit 0x0020. + +- Three byte UTF-8 sequences whose value corresponds to a surrogate + code or other reserved scalar value are not considered as an + error either. They may cause an invalid UTF-16 data (e.g., those + containing unpaired surrogates). + +*/ + +int Util::utf8ToUtf16(const unsigned char *utf8, const int utf8_length, uint16_t *utf16, const int utf16_max_length) { + + /* p moves over the output buffer. max_ptr points to the next to the last slot of the buffer. */ + uint16_t *p = utf16; + const uint16_t *max_ptr = utf16 + utf16_max_length; + + /* end_of_input points to the last byte of input as opposed to the next to the last byte. */ + unsigned char const *const end_of_input = utf8 + utf8_length - 1; + + while (utf8 <= end_of_input) { + const unsigned char c = *utf8; + if (p >= max_ptr) { + /* No more output space. */ + return -1; + } + if (c < 0x80) { + /* One byte ASCII. */ + *p++ = c; + utf8 += 1; + } else if (c < 0xC0) { + /* Follower byte without preceeding leader bytes. */ + return -1; + } else if (c < 0xE0) { + /* Two byte sequence. We need one follower byte. */ + if (end_of_input - utf8 < 1 || (((utf8[1] ^ 0x80)) & 0xC0)) { + return -1; + } + *p++ = (uint16_t)(0xCF80 + (c << 6) + utf8[1]); + utf8 += 2; + } else if (c < 0xF0) { + /* Three byte sequence. We need two follower byte. */ + if (end_of_input - utf8 < 2 || (((utf8[1] ^ 0x80) | (utf8[2] ^ 0x80)) & 0xC0)) { + return -1; + } + *p++ = (uint16_t)(0xDF80 + (c << 12) + (utf8[1] << 6) + utf8[2]); + utf8 += 3; + } else if (c < 0xF8) { + int plane; + /* Four byte sequence. We need three follower bytes. */ + if (end_of_input - utf8 < 3 || (((utf8[1] ^ 0x80) | (utf8[2] ^0x80) | (utf8[3] ^ 0x80)) & 0xC0)) { + return -1; + } + plane = (-0xC8 + (c << 2) + (utf8[1] >> 4)); + if (plane == 0) { + /* This four byte sequence is an alias that + corresponds to a Unicode scalar value in BMP. + It fits in an UTF-16 encoding unit. */ + *p++ = (uint16_t)(0xDF80 + (utf8[1] << 12) + (utf8[2] << 6) + utf8[3]); + } else if (plane <= 16) { + /* This is a legal four byte sequence that corresponds to a surrogate pair. */ + if (p + 1 >= max_ptr) { + /* No enough space on the output buffer for the pair. */ + return -1; + } + *p++ = (uint16_t)(0xE5B8 + (c << 8) + (utf8[1] << 2) + (utf8[2] >> 4)); + *p++ = (uint16_t)(0xDB80 + ((utf8[2] & 0x0F) << 6) + utf8[3]); + } else { + /* This four byte sequence is out of UTF-16 code space. */ + return -1; + } + utf8 += 4; + } else { + /* Longer sequence or unused byte. */ + return -1; + } + } + return p - utf16; +} + +/* Compare an ASCII string with an UTF-16 string */ +bool Util::utf16EqualToAscii(const char *ascii, uint16_t *utf16, int utf16Len) { + + while(*ascii != 0 && utf16Len > 0) { + if(*utf16 != (uint16_t)*ascii) { + return false; + } + utf16++; + ascii++; + utf16Len--; + } + return *ascii == 0 && utf16Len == 0; +} + +/* Convert a string using the locale encoding to an UTF-16 string */ +int Util::localeStringToUtf16(iconv_t cd, const std::string& str, uint16_t *utf16Buf, int utf16BufLen) +{ + return localeStringToUtf16(cd, str.c_str(), str.length(), utf16Buf, utf16BufLen); +} + +/* Convert a string using the locale encoding to an UTF-16 string */ +int Util::localeStringToUtf16(iconv_t cd, const char *str, size_t inbytesleft, uint16_t *utf16Buf, int utf16BufLen) +{ + size_t outbytesleft = utf16BufLen * 2; + char *ptOutBuf = (char *)utf16Buf; + char *ptStr = (char *)str; + + if(iconv(cd, &ptStr, &inbytesleft, &ptOutBuf, &outbytesleft) == 0) { + int lenWrit = ptOutBuf - (char *)utf16Buf; + if(lenWrit % 2 == 0) { + return lenWrit / 2; + } + } + return 0; +} + +/* Convert an UTF-16 string to a string using the locale encoding */ +std::string Util::utf16StringToLocale(iconv_t cd, uint16_t *utf16Buf, int lenBuf) +{ + size_t inbytesleft = lenBuf * 2; + size_t outbytesleft = inbytesleft; + char *outBuf = (char*)calloc(outbytesleft, sizeof(char)); + char *ptOutBuf = outBuf; + char *ptStr = (char *)utf16Buf; + + if(iconv(cd, &ptStr, &inbytesleft, &ptOutBuf, &outbytesleft) == 0) { + std::string retStr(outBuf, ptOutBuf - outBuf); + free(outBuf); + return retStr; + } + free(outBuf); + return ""; +} diff -U 3 -b -B -d -r -- a/util.h b/util.h --- a/util.h 2013-10-29 20:24:37.024116804 +0100 +++ b/util.h 2013-10-29 20:24:04.144116213 +0100 @@ -10,6 +10,8 @@ #define _UTIL_H__ #include +#include +#include namespace Util { bool add_mcookie(const std::string &mcookie, const char *display, @@ -19,6 +21,15 @@ long random(void); long makeseed(void); + int utf8ToUtf16(const unsigned char *utf8, const int utf8_length, + uint16_t *utf16, const int utf16_max_length); + bool utf16EqualToAscii(const char *ascii, uint16_t *utf16, int utf16Len); + int localeStringToUtf16(iconv_t cd, const std::string& str, + uint16_t *utf16Buf, int utf16BufLen); + int localeStringToUtf16(iconv_t cd, const char *str, + size_t inbytesleft, uint16_t *utf16Buf, int utf16BufLen); + + std::string utf16StringToLocale(iconv_t cd, uint16_t *utf16Buf, int lenBuf); } #endif /* _UTIL_H__ */