panel.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943
  1. /* SLiM - Simple Login Manager
  2. Copyright (C) 1997, 1998 Per Liden
  3. Copyright (C) 2004-06 Simone Rota <sip@varlock.com>
  4. Copyright (C) 2004-06 Johannes Winkelmann <jw@tks6.net>
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. */
  10. #define MODE_LOCK 1
  11. #include <sstream>
  12. #include <poll.h>
  13. #include <X11/extensions/Xrandr.h>
  14. #include "panel.h"
  15. using namespace std;
  16. Panel::Panel(Display* dpy, int scr, Window root, Cfg* config,
  17. const string& themedir, PanelType panel_mode) {
  18. /* Set display */
  19. Dpy = dpy;
  20. Scr = scr;
  21. Root = root;
  22. cfg = config;
  23. mode = panel_mode;
  24. session = "";
  25. #if MODE_LOCK
  26. if (mode == Mode_Lock) {
  27. Win = root;
  28. viewport = GetPrimaryViewport();
  29. }
  30. #endif
  31. /* Init GC */
  32. XGCValues gcv;
  33. unsigned long gcm;
  34. gcm = GCForeground|GCBackground|GCGraphicsExposures;
  35. gcv.foreground = GetColor("black");
  36. gcv.background = GetColor("white");
  37. gcv.graphics_exposures = False;
  38. #if MODE_LOCK
  39. if (mode == Mode_Lock)
  40. TextGC = XCreateGC(Dpy, Win, gcm, &gcv);
  41. else
  42. #endif
  43. TextGC = XCreateGC(Dpy, Root, gcm, &gcv);
  44. #if MODE_LOCK
  45. if (mode == Mode_Lock) {
  46. gcm = GCGraphicsExposures;
  47. gcv.graphics_exposures = False;
  48. WinGC = XCreateGC(Dpy, Win, gcm, &gcv);
  49. if (WinGC < 0) {
  50. cerr << APPNAME
  51. << ": failed to create pixmap\n.";
  52. exit(ERR_EXIT);
  53. }
  54. }
  55. #endif
  56. font = XftFontOpenName(Dpy, Scr, cfg->getOption("input_font").c_str());
  57. welcomefont = XftFontOpenName(Dpy, Scr, cfg->getOption("welcome_font").c_str());
  58. introfont = XftFontOpenName(Dpy, Scr, cfg->getOption("intro_font").c_str());
  59. enterfont = XftFontOpenName(Dpy, Scr, cfg->getOption("username_font").c_str());
  60. msgfont = XftFontOpenName(Dpy, Scr, cfg->getOption("msg_font").c_str());
  61. Visual* visual = DefaultVisual(Dpy, Scr);
  62. Colormap colormap = DefaultColormap(Dpy, Scr);
  63. /* NOTE: using XftColorAllocValue() would be a better solution. Lazy me. */
  64. XftColorAllocName(Dpy, visual, colormap, cfg->getOption("input_color").c_str(), &inputcolor);
  65. XftColorAllocName(Dpy, visual, colormap, cfg->getOption("input_shadow_color").c_str(), &inputshadowcolor);
  66. XftColorAllocName(Dpy, visual, colormap, cfg->getOption("welcome_color").c_str(), &welcomecolor);
  67. XftColorAllocName(Dpy, visual, colormap, cfg->getOption("welcome_shadow_color").c_str(), &welcomeshadowcolor);
  68. XftColorAllocName(Dpy, visual, colormap, cfg->getOption("username_color").c_str(), &entercolor);
  69. XftColorAllocName(Dpy, visual, colormap, cfg->getOption("username_shadow_color").c_str(), &entershadowcolor);
  70. XftColorAllocName(Dpy, visual, colormap, cfg->getOption("msg_color").c_str(), &msgcolor);
  71. XftColorAllocName(Dpy, visual, colormap, cfg->getOption("msg_shadow_color").c_str(), &msgshadowcolor);
  72. XftColorAllocName(Dpy, visual, colormap, cfg->getOption("intro_color").c_str(), &introcolor);
  73. XftColorAllocName(Dpy, DefaultVisual(Dpy, Scr), colormap,
  74. cfg->getOption("session_color").c_str(), &sessioncolor);
  75. XftColorAllocName(Dpy, DefaultVisual(Dpy, Scr), colormap,
  76. cfg->getOption("session_shadow_color").c_str(), &sessionshadowcolor);
  77. /* Load properties from config / theme */
  78. input_name_x = cfg->getIntOption("input_name_x");
  79. input_name_y = cfg->getIntOption("input_name_y");
  80. input_pass_x = cfg->getIntOption("input_pass_x");
  81. input_pass_y = cfg->getIntOption("input_pass_y");
  82. inputShadowXOffset = cfg->getIntOption("input_shadow_xoffset");
  83. inputShadowYOffset = cfg->getIntOption("input_shadow_yoffset");
  84. if (input_pass_x < 0 || input_pass_y < 0){ /* single inputbox mode */
  85. input_pass_x = input_name_x;
  86. input_pass_y = input_name_y;
  87. }
  88. /* Load panel and background image */
  89. string panelpng = "";
  90. panelpng = panelpng + themedir +"/panel.png";
  91. image = new Image;
  92. bool loaded = image->Read(panelpng.c_str());
  93. if (!loaded) { /* try jpeg if png failed */
  94. panelpng = themedir + "/panel.jpg";
  95. loaded = image->Read(panelpng.c_str());
  96. if (!loaded) {
  97. logStream << APPNAME
  98. << ": could not load panel image for theme '"
  99. << basename((char*)themedir.c_str()) << "'"
  100. << endl;
  101. exit(ERR_EXIT);
  102. }
  103. }
  104. Image* bg = new Image();
  105. string bgstyle = cfg->getOption("background_style");
  106. if (bgstyle != "color") {
  107. panelpng = themedir +"/background.png";
  108. loaded = bg->Read(panelpng.c_str());
  109. if (!loaded) { /* try jpeg if png failed */
  110. panelpng = themedir + "/background.jpg";
  111. loaded = bg->Read(panelpng.c_str());
  112. if (!loaded){
  113. logStream << APPNAME
  114. << ": could not load background image for theme '"
  115. << basename((char*)themedir.c_str()) << "'"
  116. << endl;
  117. exit(ERR_EXIT);
  118. }
  119. }
  120. }
  121. #if MODE_LOCK
  122. if (mode == Mode_Lock) {
  123. if (bgstyle == "stretch")
  124. bg->Resize(viewport.width, viewport.height);
  125. //bg->Resize(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)),
  126. // XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)));
  127. else if (bgstyle == "tile")
  128. bg->Tile(viewport.width, viewport.height);
  129. else if (bgstyle == "center") {
  130. string hexvalue = cfg->getOption("background_color");
  131. hexvalue = hexvalue.substr(1,6);
  132. bg->Center(viewport.width,
  133. viewport.height,
  134. hexvalue.c_str());
  135. } else { // plain color or error
  136. string hexvalue = cfg->getOption("background_color");
  137. hexvalue = hexvalue.substr(1,6);
  138. bg->Center(viewport.width,
  139. viewport.height,
  140. hexvalue.c_str());
  141. }
  142. } else {
  143. #endif
  144. if (bgstyle == "stretch") {
  145. bg->Resize(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)),
  146. XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)));
  147. } else if (bgstyle == "tile") {
  148. bg->Tile(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)),
  149. XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)));
  150. } else if (bgstyle == "center") {
  151. string hexvalue = cfg->getOption("background_color");
  152. hexvalue = hexvalue.substr(1,6);
  153. bg->Center(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)),
  154. XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)),
  155. hexvalue.c_str());
  156. } else { /* plain color or error */
  157. string hexvalue = cfg->getOption("background_color");
  158. hexvalue = hexvalue.substr(1,6);
  159. bg->Center(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)),
  160. XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)),
  161. hexvalue.c_str());
  162. }
  163. #if MODE_LOCK
  164. }
  165. #endif
  166. string cfgX = cfg->getOption("input_panel_x");
  167. string cfgY = cfg->getOption("input_panel_y");
  168. #if MODE_LOCK
  169. if (mode == Mode_Lock) {
  170. #else
  171. if (0) {
  172. #endif
  173. X = Cfg::absolutepos(cfgX, viewport.width, image->Width());
  174. Y = Cfg::absolutepos(cfgY, viewport.height, image->Height());
  175. input_name_x += X;
  176. input_name_y += Y;
  177. input_pass_x += X;
  178. input_pass_y += Y;
  179. } else {
  180. X = Cfg::absolutepos(cfgX, XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), image->Width());
  181. Y = Cfg::absolutepos(cfgY, XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)), image->Height());
  182. }
  183. if (mode == Mode_Lock) {
  184. /* Merge image into background without crop */
  185. image->Merge_non_crop(bg, X, Y);
  186. PanelPixmap = image->createPixmap(Dpy, Scr, Win);
  187. } else {
  188. /* Merge image into background */
  189. image->Merge(bg, X, Y);
  190. PanelPixmap = image->createPixmap(Dpy, Scr, Root);
  191. }
  192. delete bg;
  193. /* Read (and substitute vars in) the welcome message */
  194. welcome_message = cfg->getWelcomeMessage();
  195. intro_message = cfg->getOption("intro_msg");
  196. #if MODE_LOCK
  197. if (mode == Mode_Lock) {
  198. SetName(getenv("USER"));
  199. field = Get_Passwd;
  200. OnExpose();
  201. }
  202. #endif
  203. }
  204. Panel::~Panel() {
  205. XftColorFree(Dpy, DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr), &inputcolor);
  206. XftColorFree(Dpy, DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr), &msgcolor);
  207. XftColorFree(Dpy, DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr), &welcomecolor);
  208. XftColorFree(Dpy, DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr), &entercolor);
  209. XftColorFree(Dpy, DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr), &sessioncolor);
  210. XftColorFree(Dpy, DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr), &sessionshadowcolor);
  211. XFreeGC(Dpy, TextGC);
  212. XftFontClose(Dpy, font);
  213. XftFontClose(Dpy, msgfont);
  214. XftFontClose(Dpy, introfont);
  215. XftFontClose(Dpy, welcomefont);
  216. XftFontClose(Dpy, enterfont);
  217. #if MODE_LOCK
  218. if (mode == Mode_Lock)
  219. XFreeGC(Dpy, WinGC);
  220. #endif
  221. delete image;
  222. }
  223. void Panel::OpenPanel() {
  224. /* Create window */
  225. Win = XCreateSimpleWindow(Dpy, Root, X, Y,
  226. image->Width(),
  227. image->Height(),
  228. 0, GetColor("white"), GetColor("white"));
  229. /* Events */
  230. XSelectInput(Dpy, Win, ExposureMask | KeyPressMask);
  231. /* Set background */
  232. XSetWindowBackgroundPixmap(Dpy, Win, PanelPixmap);
  233. /* Show window */
  234. XMapWindow(Dpy, Win);
  235. XMoveWindow(Dpy, Win, X, Y); /* override wm positioning (for tests) */
  236. /* Grab keyboard */
  237. XGrabKeyboard(Dpy, Win, False, GrabModeAsync, GrabModeAsync, CurrentTime);
  238. XFlush(Dpy);
  239. }
  240. void Panel::ClosePanel() {
  241. XUngrabKeyboard(Dpy, CurrentTime);
  242. XUnmapWindow(Dpy, Win);
  243. XDestroyWindow(Dpy, Win);
  244. XFlush(Dpy);
  245. }
  246. void Panel::ClearPanel() {
  247. session = "";
  248. Reset();
  249. XClearWindow(Dpy, Root);
  250. XClearWindow(Dpy, Win);
  251. Cursor(SHOW);
  252. ShowText();
  253. XFlush(Dpy);
  254. }
  255. void Panel::WrongPassword(int timeout) {
  256. string message;
  257. XGlyphInfo extents;
  258. #if 0
  259. if (CapsLockOn)
  260. message = cfg->getOption("passwd_feedback_capslock");
  261. else
  262. #endif
  263. message = cfg->getOption("passwd_feedback_msg");
  264. XftDraw *draw = XftDrawCreate(Dpy, Win,
  265. DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr));
  266. XftTextExtents8(Dpy, msgfont, reinterpret_cast<const XftChar8*>(message.c_str()),
  267. message.length(), &extents);
  268. string cfgX = cfg->getOption("passwd_feedback_x");
  269. string cfgY = cfg->getOption("passwd_feedback_y");
  270. int shadowXOffset = cfg->getIntOption("msg_shadow_xoffset");
  271. int shadowYOffset = cfg->getIntOption("msg_shadow_yoffset");
  272. int msg_x = Cfg::absolutepos(cfgX, XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), extents.width);
  273. int msg_y = Cfg::absolutepos(cfgY, XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)), extents.height);
  274. OnExpose();
  275. SlimDrawString8(draw, &msgcolor, msgfont, msg_x, msg_y, message,
  276. &msgshadowcolor, shadowXOffset, shadowYOffset);
  277. if (cfg->getOption("bell") == "1")
  278. XBell(Dpy, 100);
  279. XFlush(Dpy);
  280. sleep(timeout);
  281. ResetPasswd();
  282. OnExpose();
  283. // The message should stay on the screen even after the password field is
  284. // cleared, methinks. I don't like this solution, but it works.
  285. SlimDrawString8(draw, &msgcolor, msgfont, msg_x, msg_y, message,
  286. &msgshadowcolor, shadowXOffset, shadowYOffset);
  287. XSync(Dpy, True);
  288. XftDrawDestroy(draw);
  289. }
  290. void Panel::Message(const string& text) {
  291. string cfgX, cfgY;
  292. XGlyphInfo extents;
  293. XftDraw *draw;
  294. #if MODE_LOCK
  295. if (mode == Mode_Lock)
  296. draw = XftDrawCreate(Dpy, Win,
  297. DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr));
  298. else
  299. #endif
  300. draw = XftDrawCreate(Dpy, Root,
  301. DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr));
  302. XftTextExtents8(Dpy, msgfont,
  303. reinterpret_cast<const XftChar8*>(text.c_str()),
  304. text.length(), &extents);
  305. cfgX = cfg->getOption("msg_x");
  306. cfgY = cfg->getOption("msg_y");
  307. int shadowXOffset = cfg->getIntOption("msg_shadow_xoffset");
  308. int shadowYOffset = cfg->getIntOption("msg_shadow_yoffset");
  309. int msg_x, msg_y;
  310. #if MODE_LOCK
  311. if (mode == Mode_Lock) {
  312. msg_x = Cfg::absolutepos(cfgX, viewport.width, extents.width);
  313. msg_y = Cfg::absolutepos(cfgY, viewport.height, extents.height);
  314. } else {
  315. #endif
  316. msg_x = Cfg::absolutepos(cfgX, XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), extents.width);
  317. msg_y = Cfg::absolutepos(cfgY, XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)), extents.height);
  318. #if MODE_LOCK
  319. }
  320. #endif
  321. SlimDrawString8 (draw, &msgcolor, msgfont, msg_x, msg_y,
  322. text,
  323. &msgshadowcolor,
  324. shadowXOffset, shadowYOffset);
  325. XFlush(Dpy);
  326. XftDrawDestroy(draw);
  327. }
  328. void Panel::Error(const string& text) {
  329. ClosePanel();
  330. Message(text);
  331. sleep(ERROR_DURATION);
  332. OpenPanel();
  333. ClearPanel();
  334. }
  335. unsigned long Panel::GetColor(const char* colorname) {
  336. XColor color;
  337. XWindowAttributes attributes;
  338. #if MODE_LOCK
  339. if (mode == Mode_Lock)
  340. XGetWindowAttributes(Dpy, Win, &attributes);
  341. else
  342. #endif
  343. XGetWindowAttributes(Dpy, Root, &attributes);
  344. color.pixel = 0;
  345. if(!XParseColor(Dpy, attributes.colormap, colorname, &color))
  346. logStream << APPNAME << ": can't parse color " << colorname << endl;
  347. else if(!XAllocColor(Dpy, attributes.colormap, &color))
  348. logStream << APPNAME << ": can't allocate color " << colorname << endl;
  349. return color.pixel;
  350. }
  351. void Panel::Cursor(int visible) {
  352. const char* text;
  353. int xx, yy, y2, cheight;
  354. const char* txth = "Wj"; /* used to get cursor height */
  355. #if MODE_LOCK
  356. if (this->mode == Mode_Lock) {
  357. #else
  358. if (0) {
  359. #endif
  360. text = HiddenPasswdBuffer.c_str();
  361. xx = input_pass_x;
  362. yy = input_pass_y;
  363. } else {
  364. switch(field) {
  365. case Get_Passwd:
  366. text = HiddenPasswdBuffer.c_str();
  367. xx = input_pass_x;
  368. yy = input_pass_y;
  369. break;
  370. case Get_Name:
  371. text = NameBuffer.c_str();
  372. xx = input_name_x;
  373. yy = input_name_y;
  374. break;
  375. }
  376. }
  377. XGlyphInfo extents;
  378. XftTextExtents8(Dpy, font, (XftChar8*)txth, strlen(txth), &extents);
  379. cheight = extents.height;
  380. y2 = yy - extents.y + extents.height;
  381. XftTextExtents8(Dpy, font, (XftChar8*)text, strlen(text), &extents);
  382. xx += extents.width;
  383. if(visible == SHOW) {
  384. #if MODE_LOCK
  385. if (mode == Mode_Lock) {
  386. xx += viewport.x;
  387. yy += viewport.y;
  388. y2 += viewport.y;
  389. }
  390. #endif
  391. XSetForeground(Dpy, TextGC,
  392. GetColor(cfg->getOption("input_color").c_str()));
  393. XDrawLine(Dpy, Win, TextGC,
  394. xx+1, yy-cheight,
  395. xx+1, y2);
  396. } else {
  397. #if MODE_LOCK
  398. if (mode == Mode_Lock) {
  399. ApplyBackground(Rectangle(xx+1, yy-cheight,
  400. 1, y2-(yy-cheight)+1));
  401. }
  402. else
  403. #endif
  404. XClearArea(Dpy, Win, xx+1, yy-cheight,
  405. 1, y2-(yy-cheight)+1, false);
  406. }
  407. }
  408. void Panel::EventHandler(const Panel::FieldType& curfield) {
  409. XEvent event;
  410. field = curfield;
  411. bool loop = true;
  412. if (mode == Mode_DM)
  413. OnExpose();
  414. struct pollfd x11_pfd = {0};
  415. x11_pfd.fd = ConnectionNumber(Dpy);
  416. x11_pfd.events = POLLIN;
  417. while (loop) {
  418. if (XPending(Dpy) || poll(&x11_pfd, 1, -1) > 0) {
  419. while(XPending(Dpy)) {
  420. XNextEvent(Dpy, &event);
  421. switch(event.type) {
  422. case Expose:
  423. OnExpose();
  424. break;
  425. case KeyPress:
  426. loop=OnKeyPress(event);
  427. break;
  428. }
  429. }
  430. }
  431. }
  432. return;
  433. }
  434. void Panel::OnExpose(void) {
  435. XftDraw *draw = XftDrawCreate(Dpy, Win,
  436. DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr));
  437. #if MODE_LOCK
  438. if (mode == Mode_Lock) {
  439. ApplyBackground();
  440. } else
  441. #endif
  442. XClearWindow(Dpy, Win);
  443. if (input_pass_x != input_name_x || input_pass_y != input_name_y){
  444. SlimDrawString8 (draw, &inputcolor, font, input_name_x, input_name_y,
  445. NameBuffer,
  446. &inputshadowcolor,
  447. inputShadowXOffset, inputShadowYOffset);
  448. SlimDrawString8 (draw, &inputcolor, font, input_pass_x, input_pass_y,
  449. HiddenPasswdBuffer,
  450. &inputshadowcolor,
  451. inputShadowXOffset, inputShadowYOffset);
  452. } else { /*single input mode */
  453. switch(field) {
  454. case Get_Passwd:
  455. SlimDrawString8 (draw, &inputcolor, font,
  456. input_pass_x, input_pass_y,
  457. HiddenPasswdBuffer,
  458. &inputshadowcolor,
  459. inputShadowXOffset, inputShadowYOffset);
  460. break;
  461. case Get_Name:
  462. SlimDrawString8 (draw, &inputcolor, font,
  463. input_name_x, input_name_y,
  464. NameBuffer,
  465. &inputshadowcolor,
  466. inputShadowXOffset, inputShadowYOffset);
  467. break;
  468. }
  469. }
  470. XftDrawDestroy (draw);
  471. Cursor(SHOW);
  472. ShowText();
  473. }
  474. bool Panel::OnKeyPress(XEvent& event) {
  475. char ascii;
  476. KeySym keysym;
  477. XComposeStatus compstatus;
  478. int xx;
  479. int yy;
  480. string text;
  481. string formerString = "";
  482. XLookupString(&event.xkey, &ascii, 1, &keysym, &compstatus);
  483. switch(keysym){
  484. case XK_F1:
  485. SwitchSession();
  486. return true;
  487. case XK_F11:
  488. /* Take a screenshot */
  489. system(cfg->getOption("screenshot_cmd").c_str());
  490. return true;
  491. case XK_Return:
  492. case XK_KP_Enter:
  493. if (field==Get_Name){
  494. /* Don't allow an empty username */
  495. if (NameBuffer.empty()) return true;
  496. if (NameBuffer==CONSOLE_STR){
  497. action = Console;
  498. } else if (NameBuffer==HALT_STR){
  499. action = Halt;
  500. } else if (NameBuffer==REBOOT_STR){
  501. action = Reboot;
  502. } else if (NameBuffer==SUSPEND_STR){
  503. action = Suspend;
  504. } else if (NameBuffer==EXIT_STR){
  505. action = Exit;
  506. } else{
  507. if (mode == Mode_DM)
  508. action = Login;
  509. else
  510. action = Lock;
  511. }
  512. };
  513. return false;
  514. default:
  515. break;
  516. };
  517. Cursor(HIDE);
  518. switch(keysym){
  519. case XK_Delete:
  520. case XK_BackSpace:
  521. switch(field) {
  522. case GET_NAME:
  523. if (! NameBuffer.empty()){
  524. formerString=NameBuffer;
  525. NameBuffer.erase(--NameBuffer.end());
  526. };
  527. break;
  528. case GET_PASSWD:
  529. if (! PasswdBuffer.empty()){
  530. formerString=HiddenPasswdBuffer;
  531. PasswdBuffer.erase(--PasswdBuffer.end());
  532. HiddenPasswdBuffer.erase(--HiddenPasswdBuffer.end());
  533. };
  534. break;
  535. };
  536. break;
  537. case XK_w:
  538. case XK_u:
  539. if (reinterpret_cast<XKeyEvent&>(event).state & ControlMask) {
  540. switch(field) {
  541. case Get_Passwd:
  542. formerString = HiddenPasswdBuffer;
  543. HiddenPasswdBuffer.clear();
  544. PasswdBuffer.clear();
  545. break;
  546. case Get_Name:
  547. formerString = NameBuffer;
  548. NameBuffer.clear();
  549. break;
  550. };
  551. break;
  552. }
  553. /* Deliberate fall-through */
  554. default:
  555. if (isprint(ascii) && (keysym < XK_Shift_L || keysym > XK_Hyper_R)){
  556. switch(field) {
  557. case GET_NAME:
  558. formerString=NameBuffer;
  559. if (NameBuffer.length() < INPUT_MAXLENGTH_NAME-1){
  560. NameBuffer.append(&ascii,1);
  561. };
  562. break;
  563. case GET_PASSWD:
  564. formerString=HiddenPasswdBuffer;
  565. if (PasswdBuffer.length() < INPUT_MAXLENGTH_PASSWD-1){
  566. PasswdBuffer.append(&ascii,1);
  567. HiddenPasswdBuffer.append("*");
  568. };
  569. break;
  570. };
  571. };
  572. break;
  573. };
  574. XGlyphInfo extents;
  575. XftDraw *draw = XftDrawCreate(Dpy, Win,
  576. DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr));
  577. switch(field) {
  578. case Get_Name:
  579. text = NameBuffer;
  580. xx = input_name_x;
  581. yy = input_name_y;
  582. break;
  583. case Get_Passwd:
  584. text = HiddenPasswdBuffer;
  585. xx = input_pass_x;
  586. yy = input_pass_y;
  587. break;
  588. }
  589. if (!formerString.empty()){
  590. const char* txth = "Wj"; /* get proper maximum height ? */
  591. XftTextExtents8(Dpy, font, reinterpret_cast<const XftChar8*>(txth), strlen(txth), &extents);
  592. int maxHeight = extents.height;
  593. XftTextExtents8(Dpy, font, reinterpret_cast<const XftChar8*>(formerString.c_str()),
  594. formerString.length(), &extents);
  595. int maxLength = extents.width;
  596. #if MODE_LOCK
  597. if (mode == Mode_Lock)
  598. ApplyBackground(Rectangle(input_pass_x - 3,
  599. input_pass_y - maxHeight - 3,
  600. maxLength + 6, maxHeight + 6));
  601. else
  602. #endif
  603. XClearArea(Dpy, Win, xx - 3, yy-maxHeight - 3,
  604. maxLength + 6, maxHeight + 6, false);
  605. }
  606. if (!text.empty()) {
  607. SlimDrawString8 (draw, &inputcolor, font, xx, yy,
  608. text,
  609. &inputshadowcolor,
  610. inputShadowXOffset, inputShadowYOffset);
  611. }
  612. XftDrawDestroy (draw);
  613. Cursor(SHOW);
  614. return true;
  615. }
  616. /* Draw welcome and "enter username" message */
  617. void Panel::ShowText(){
  618. string cfgX, cfgY;
  619. XGlyphInfo extents;
  620. bool singleInputMode =
  621. input_name_x == input_pass_x &&
  622. input_name_y == input_pass_y;
  623. XftDraw *draw = XftDrawCreate(Dpy, Win,
  624. DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr));
  625. /* welcome message */
  626. XftTextExtents8(Dpy, welcomefont, (XftChar8*)welcome_message.c_str(),
  627. strlen(welcome_message.c_str()), &extents);
  628. cfgX = cfg->getOption("welcome_x");
  629. cfgY = cfg->getOption("welcome_y");
  630. int shadowXOffset = cfg->getIntOption("welcome_shadow_xoffset");
  631. int shadowYOffset = cfg->getIntOption("welcome_shadow_yoffset");
  632. welcome_x = Cfg::absolutepos(cfgX, image->Width(), extents.width);
  633. welcome_y = Cfg::absolutepos(cfgY, image->Height(), extents.height);
  634. if (welcome_x >= 0 && welcome_y >= 0) {
  635. SlimDrawString8 (draw, &welcomecolor, welcomefont,
  636. welcome_x, welcome_y,
  637. welcome_message,
  638. &welcomeshadowcolor, shadowXOffset, shadowYOffset);
  639. }
  640. /* Enter username-password message */
  641. string msg;
  642. if ((!singleInputMode|| field == Get_Passwd) && mode == Mode_DM) {
  643. msg = cfg->getOption("password_msg");
  644. XftTextExtents8(Dpy, enterfont, (XftChar8*)msg.c_str(),
  645. strlen(msg.c_str()), &extents);
  646. cfgX = cfg->getOption("password_x");
  647. cfgY = cfg->getOption("password_y");
  648. int shadowXOffset = cfg->getIntOption("username_shadow_xoffset");
  649. int shadowYOffset = cfg->getIntOption("username_shadow_yoffset");
  650. password_x = Cfg::absolutepos(cfgX, image->Width(), extents.width);
  651. password_y = Cfg::absolutepos(cfgY, image->Height(), extents.height);
  652. if (password_x >= 0 && password_y >= 0){
  653. SlimDrawString8 (draw, &entercolor, enterfont, password_x, password_y,
  654. msg, &entershadowcolor, shadowXOffset, shadowYOffset);
  655. }
  656. }
  657. if (!singleInputMode|| field == Get_Name) {
  658. msg = cfg->getOption("username_msg");
  659. XftTextExtents8(Dpy, enterfont, (XftChar8*)msg.c_str(),
  660. strlen(msg.c_str()), &extents);
  661. cfgX = cfg->getOption("username_x");
  662. cfgY = cfg->getOption("username_y");
  663. int shadowXOffset = cfg->getIntOption("username_shadow_xoffset");
  664. int shadowYOffset = cfg->getIntOption("username_shadow_yoffset");
  665. username_x = Cfg::absolutepos(cfgX, image->Width(), extents.width);
  666. username_y = Cfg::absolutepos(cfgY, image->Height(), extents.height);
  667. if (username_x >= 0 && username_y >= 0){
  668. SlimDrawString8 (draw, &entercolor, enterfont, username_x, username_y,
  669. msg, &entershadowcolor, shadowXOffset, shadowYOffset);
  670. }
  671. }
  672. XftDrawDestroy(draw);
  673. #if MODE_LOCK
  674. if (mode == Mode_Lock) {
  675. // If only the password box is visible, draw the user name somewhere too
  676. string user_msg = "User: " + GetName();
  677. int show_username = cfg->getIntOption("show_username");
  678. if (singleInputMode && show_username) {
  679. Message(user_msg);
  680. }
  681. }
  682. #endif
  683. }
  684. string Panel::getSession() {
  685. return session;
  686. }
  687. /* choose next available session type */
  688. void Panel::SwitchSession() {
  689. session = cfg->nextSession(session);
  690. if (session.size() > 0) {
  691. ShowSession();
  692. }
  693. }
  694. /* Display session type on the screen */
  695. void Panel::ShowSession() {
  696. string msg_x, msg_y;
  697. XClearWindow(Dpy, Root);
  698. string currsession = cfg->getOption("session_msg") + " " + session;
  699. XGlyphInfo extents;
  700. sessionfont = XftFontOpenName(Dpy, Scr, cfg->getOption("session_font").c_str());
  701. XftDraw *draw = XftDrawCreate(Dpy, Root,
  702. DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr));
  703. XftTextExtents8(Dpy, sessionfont, reinterpret_cast<const XftChar8*>(currsession.c_str()),
  704. currsession.length(), &extents);
  705. msg_x = cfg->getOption("session_x");
  706. msg_y = cfg->getOption("session_y");
  707. int x = Cfg::absolutepos(msg_x, XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), extents.width);
  708. int y = Cfg::absolutepos(msg_y, XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)), extents.height);
  709. int shadowXOffset = cfg->getIntOption("session_shadow_xoffset");
  710. int shadowYOffset = cfg->getIntOption("session_shadow_yoffset");
  711. SlimDrawString8(draw, &sessioncolor, sessionfont, x, y,
  712. currsession,
  713. &sessionshadowcolor,
  714. shadowXOffset, shadowYOffset);
  715. XFlush(Dpy);
  716. XftDrawDestroy(draw);
  717. }
  718. void Panel::SlimDrawString8(XftDraw *d, XftColor *color, XftFont *font,
  719. int x, int y, const string& str,
  720. XftColor* shadowColor,
  721. int xOffset, int yOffset)
  722. {
  723. int calc_x = 0;
  724. int calc_y = 0;
  725. #if MODE_LOCK
  726. if (mode == Mode_Lock) {
  727. calc_x = viewport.x;
  728. calc_y = viewport.y;
  729. }
  730. #endif
  731. if (xOffset && yOffset) {
  732. XftDrawStringUtf8(d, shadowColor, font,
  733. x + xOffset + calc_x,
  734. y + yOffset + calc_y,
  735. reinterpret_cast<const FcChar8*>(str.c_str()),
  736. str.length());
  737. }
  738. XftDrawStringUtf8(d, color, font,
  739. x + calc_x,
  740. y + calc_y,
  741. reinterpret_cast<const FcChar8*>(str.c_str()),
  742. str.length());
  743. }
  744. Panel::ActionType Panel::getAction(void) const{
  745. return action;
  746. };
  747. void Panel::Reset(void){
  748. ResetName();
  749. ResetPasswd();
  750. };
  751. void Panel::ResetName(void){
  752. NameBuffer.clear();
  753. };
  754. void Panel::ResetPasswd(void){
  755. PasswdBuffer.clear();
  756. HiddenPasswdBuffer.clear();
  757. };
  758. void Panel::SetName(const string& name){
  759. NameBuffer=name;
  760. if (mode == Mode_DM)
  761. action = Login;
  762. else
  763. action = Lock;
  764. };
  765. const string& Panel::GetName(void) const{
  766. return NameBuffer;
  767. };
  768. const string& Panel::GetPasswd(void) const{
  769. return PasswdBuffer;
  770. };
  771. Rectangle Panel::GetPrimaryViewport() {
  772. Rectangle fallback;
  773. Rectangle result;
  774. RROutput primary;
  775. XRROutputInfo *primary_info;
  776. XRRScreenResources *resources;
  777. XRRCrtcInfo *crtc_info;
  778. fallback.x = 0;
  779. fallback.y = 0;
  780. fallback.width = DisplayWidth(Dpy, Scr);
  781. fallback.height = DisplayHeight(Dpy, Scr);
  782. primary = XRRGetOutputPrimary(Dpy, Win);
  783. if (!primary) {
  784. return fallback;
  785. }
  786. resources = XRRGetScreenResources(Dpy, Win);
  787. if (!resources)
  788. return fallback;
  789. primary_info = XRRGetOutputInfo(Dpy, resources, primary);
  790. if (!primary_info) {
  791. XRRFreeScreenResources(resources);
  792. return fallback;
  793. }
  794. crtc_info = XRRGetCrtcInfo(Dpy, resources, primary_info->crtc);
  795. if (!crtc_info) {
  796. XRRFreeOutputInfo(primary_info);
  797. XRRFreeScreenResources(resources);
  798. return fallback;
  799. }
  800. result.x = crtc_info->x;
  801. result.y = crtc_info->y;
  802. result.width = crtc_info->width;
  803. result.height = crtc_info->height;
  804. XRRFreeCrtcInfo(crtc_info);
  805. XRRFreeOutputInfo(primary_info);
  806. XRRFreeScreenResources(resources);
  807. return result;
  808. }
  809. void Panel::ApplyBackground(Rectangle rect) {
  810. int ret = 0;
  811. if (rect.is_empty()) {
  812. rect.x = 0;
  813. rect.y = 0;
  814. rect.width = viewport.width;
  815. rect.height = viewport.height;
  816. }
  817. ret = XCopyArea(Dpy, PanelPixmap, Win, WinGC,
  818. rect.x, rect.y, rect.width, rect.height,
  819. viewport.x + rect.x, viewport.y + rect.y);
  820. if (!ret)
  821. cerr << APPNAME << ": failed to put pixmap on the screen\n.";
  822. }