/* SLiM - Simple Login Manager
|
|
Copyright (C) 1997, 1998 Per Liden
|
|
Copyright (C) 2004-06 Simone Rota <sip@varlock.com>
|
|
Copyright (C) 2004-06 Johannes Winkelmann <jw@tks6.net>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
*/
|
|
|
|
|
|
#include "Panel.h"
|
|
|
|
|
|
using namespace Schlimm;
|
|
|
|
|
|
Panel::Panel(
|
|
Display* dpy,
|
|
int scr,
|
|
Window root,
|
|
Schlimm::Config* config,
|
|
const std::string& themedir,
|
|
PanelType panel_mode
|
|
)
|
|
{
|
|
/* Set display */
|
|
this->display = dpy;
|
|
this->screen = scr;
|
|
this->root_window = root;
|
|
this->config = config;
|
|
mode = panel_mode;
|
|
|
|
session_name = "";
|
|
session_exec = "";
|
|
if (mode == Mode_Lock) {
|
|
this->window = root;
|
|
viewport = this->getPrimaryViewport();
|
|
}
|
|
|
|
/* Init GC */
|
|
XGCValues gcv;
|
|
unsigned long gcm;
|
|
gcm = GCForeground|GCBackground|GCGraphicsExposures;
|
|
gcv.foreground = this->getColor("black");
|
|
gcv.background = this->getColor("white");
|
|
gcv.graphics_exposures = False;
|
|
if (mode == Mode_Lock) {
|
|
this->text_gc = XCreateGC(this->display, this->window, gcm, &gcv);
|
|
} else {
|
|
this->text_gc = XCreateGC(this->display, this->root_window, gcm, &gcv);
|
|
}
|
|
|
|
if (mode == Mode_Lock) {
|
|
gcm = GCGraphicsExposures;
|
|
gcv.graphics_exposures = False;
|
|
this->win_gc = XCreateGC(this->display, this->window, gcm, &gcv);
|
|
if (this->win_gc < 0) {
|
|
Log::log("Failed to create pixmap!");
|
|
exit(ERR_EXIT);
|
|
}
|
|
}
|
|
|
|
font = XftFontOpenName(this->display, this->screen, this->config->getOption("input_font").c_str());
|
|
welcomefont = XftFontOpenName(this->display, this->screen, this->config->getOption("welcome_font").c_str());
|
|
introfont = XftFontOpenName(this->display, this->screen, this->config->getOption("intro_font").c_str());
|
|
enterfont = XftFontOpenName(this->display, this->screen, this->config->getOption("username_font").c_str());
|
|
msgfont = XftFontOpenName(this->display, this->screen, this->config->getOption("msg_font").c_str());
|
|
|
|
Visual* visual = DefaultVisual(this->display, this->screen);
|
|
Colormap colormap = DefaultColormap(this->display, this->screen);
|
|
/* NOTE: using XftColorAllocValue() would be a better solution. Lazy me. */
|
|
XftColorAllocName(this->display, visual, colormap, this->config->getOption("input_color").c_str(), &inputcolor);
|
|
XftColorAllocName(this->display, visual, colormap, this->config->getOption("input_shadow_color").c_str(), &inputshadowcolor);
|
|
XftColorAllocName(this->display, visual, colormap, this->config->getOption("welcome_color").c_str(), &welcomecolor);
|
|
XftColorAllocName(this->display, visual, colormap, this->config->getOption("welcome_shadow_color").c_str(), &welcomeshadowcolor);
|
|
XftColorAllocName(this->display, visual, colormap, this->config->getOption("username_color").c_str(), &entercolor);
|
|
XftColorAllocName(this->display, visual, colormap, this->config->getOption("username_shadow_color").c_str(), &entershadowcolor);
|
|
XftColorAllocName(this->display, visual, colormap, this->config->getOption("msg_color").c_str(), &msgcolor);
|
|
XftColorAllocName(this->display, visual, colormap, this->config->getOption("msg_shadow_color").c_str(), &msgshadowcolor);
|
|
XftColorAllocName(this->display, visual, colormap, this->config->getOption("intro_color").c_str(), &introcolor);
|
|
XftColorAllocName(this->display, visual, colormap,
|
|
this->config->getOption("session_color").c_str(), &sessioncolor);
|
|
XftColorAllocName(this->display, visual, colormap,
|
|
this->config->getOption("session_shadow_color").c_str(), &sessionshadowcolor);
|
|
|
|
/* Load properties from config / theme */
|
|
input_name_x = this->config->getIntOption("input_name_x");
|
|
input_name_y = this->config->getIntOption("input_name_y");
|
|
input_pass_x = this->config->getIntOption("input_pass_x");
|
|
input_pass_y = this->config->getIntOption("input_pass_y");
|
|
inputShadowXOffset = this->config->getIntOption("input_shadow_xoffset");
|
|
inputShadowYOffset = this->config->getIntOption("input_shadow_yoffset");
|
|
|
|
if (input_pass_x < 0 || input_pass_y < 0){ /* single inputbox mode */
|
|
input_pass_x = input_name_x;
|
|
input_pass_y = input_name_y;
|
|
}
|
|
|
|
/* Load panel and background image */
|
|
std::string panelpng = "";
|
|
panelpng = panelpng + themedir +"/panel.png";
|
|
image = new Schlimm::Image;
|
|
bool panel_image_loaded = image->Read(panelpng.c_str());
|
|
if (!panel_image_loaded) { /* try jpeg if png failed */
|
|
panelpng = themedir + "/panel.jpg";
|
|
panel_image_loaded = image->Read(panelpng.c_str());
|
|
if (!panel_image_loaded) {
|
|
Log::log(
|
|
fmt::format(
|
|
"Could not load the panel image file from directory {}!",
|
|
themedir
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<Schlimm::Image> bg = std::make_unique<Schlimm::Image>();
|
|
if (bg == nullptr) {
|
|
Log::log(
|
|
"Error initialising image reading module!"
|
|
);
|
|
} else {
|
|
std::string bgstyle = this->config->getOption("background_style");
|
|
bool background_image_loaded = false;
|
|
if (bgstyle != "color") {
|
|
panelpng = themedir +"/background.png";
|
|
background_image_loaded = bg->Read(panelpng.c_str());
|
|
if (!background_image_loaded) { /* try jpeg if png failed */
|
|
panelpng = themedir + "/background.jpg";
|
|
background_image_loaded = bg->Read(panelpng.c_str());
|
|
if (!background_image_loaded) {
|
|
Log::log(
|
|
fmt::format(
|
|
"Could not load the background image file from directory '{}'!",
|
|
themedir
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (background_image_loaded) {
|
|
if (mode == Mode_Lock) {
|
|
if (bgstyle == "stretch") {
|
|
bg->Resize(viewport.width, viewport.height);
|
|
//bg->Resize(XWidthOfScreen(ScreenOfDisplay(this->display, this->screen)),
|
|
// XHeightOfScreen(ScreenOfDisplay(this->display, this->screen)));
|
|
} else if (bgstyle == "tile") {
|
|
bg->Tile(viewport.width, viewport.height);
|
|
} else if (bgstyle == "center") {
|
|
std::string hexvalue = this->config->getOption("background_color");
|
|
hexvalue = hexvalue.substr(1,6);
|
|
bg->Center(
|
|
viewport.width,
|
|
viewport.height,
|
|
hexvalue.c_str()
|
|
);
|
|
} else { // plain color or error
|
|
std::string hexvalue = this->config->getOption("background_color");
|
|
hexvalue = hexvalue.substr(1,6);
|
|
bg->Center(
|
|
viewport.width,
|
|
viewport.height,
|
|
hexvalue.c_str()
|
|
);
|
|
}
|
|
} else {
|
|
if (bgstyle == "stretch") {
|
|
bg->Resize(
|
|
XWidthOfScreen(ScreenOfDisplay(this->display, this->screen)),
|
|
XHeightOfScreen(ScreenOfDisplay(this->display, this->screen))
|
|
);
|
|
} else if (bgstyle == "tile") {
|
|
bg->Tile(
|
|
XWidthOfScreen(ScreenOfDisplay(this->display, this->screen)),
|
|
XHeightOfScreen(ScreenOfDisplay(this->display, this->screen))
|
|
);
|
|
} else if (bgstyle == "center") {
|
|
std::string hexvalue = this->config->getOption("background_color");
|
|
hexvalue = hexvalue.substr(1,6);
|
|
bg->Center(
|
|
XWidthOfScreen(ScreenOfDisplay(this->display, this->screen)),
|
|
XHeightOfScreen(ScreenOfDisplay(this->display, this->screen)),
|
|
hexvalue.c_str()
|
|
);
|
|
} else { /* plain color or error */
|
|
std::string hexvalue = this->config->getOption("background_color");
|
|
hexvalue = hexvalue.substr(1,6);
|
|
bg->Center(
|
|
XWidthOfScreen(ScreenOfDisplay(this->display, this->screen)),
|
|
XHeightOfScreen(ScreenOfDisplay(this->display, this->screen)),
|
|
hexvalue.c_str()
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string cfgX = this->config->getOption("input_panel_x");
|
|
std::string cfgY = this->config->getOption("input_panel_y");
|
|
|
|
if (panel_image_loaded) {
|
|
if (mode == Mode_Lock) {
|
|
X = Schlimm::Config::absolutepos(cfgX, viewport.width, image->Width());
|
|
Y = Schlimm::Config::absolutepos(cfgY, viewport.height, image->Height());
|
|
|
|
input_name_x += X;
|
|
input_name_y += Y;
|
|
input_pass_x += X;
|
|
input_pass_y += Y;
|
|
} else {
|
|
X = Schlimm::Config::absolutepos(cfgX, XWidthOfScreen(ScreenOfDisplay(this->display, this->screen)), image->Width());
|
|
Y = Schlimm::Config::absolutepos(cfgY, XHeightOfScreen(ScreenOfDisplay(this->display, this->screen)), image->Height());
|
|
}
|
|
|
|
if (mode == Mode_Lock) {
|
|
/* Merge image into background without crop */
|
|
image->Merge_non_crop(bg.get(), X, Y);
|
|
this->panel_pixmap = image->createPixmap(this->display, this->screen, this->window);
|
|
} else {
|
|
/* Merge image into background */
|
|
image->Merge(bg.get(), X, Y);
|
|
this->panel_pixmap = image->createPixmap(this->display, this->screen, this->root_window);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Read (and substitute vars in) the welcome message */
|
|
welcome_message = this->config->getWelcomeMessage();
|
|
intro_message = this->config->getOption("intro_msg");
|
|
|
|
if (mode == Mode_Lock) {
|
|
this->setName(getenv("USER"));
|
|
field = Get_Passwd;
|
|
this->onExpose();
|
|
}
|
|
}
|
|
|
|
|
|
Panel::~Panel()
|
|
{
|
|
Visual* visual = DefaultVisual(this->display, this->screen);
|
|
Colormap colormap = DefaultColormap(this->display, this->screen);
|
|
|
|
XftColorFree(this->display, visual, colormap, &inputcolor);
|
|
XftColorFree(this->display, visual, colormap, &inputshadowcolor);
|
|
XftColorFree(this->display, visual, colormap, &welcomecolor);
|
|
XftColorFree(this->display, visual, colormap, &welcomeshadowcolor);
|
|
XftColorFree(this->display, visual, colormap, &entercolor);
|
|
XftColorFree(this->display, visual, colormap, &entershadowcolor);
|
|
XftColorFree(this->display, visual, colormap, &msgcolor);
|
|
XftColorFree(this->display, visual, colormap, &msgshadowcolor);
|
|
XftColorFree(this->display, visual, colormap, &introcolor);
|
|
XftColorFree(this->display, visual, colormap, &sessioncolor);
|
|
XftColorFree(this->display, visual, colormap, &sessionshadowcolor);
|
|
|
|
XFreeGC(this->display, this->text_gc);
|
|
XftFontClose(this->display, font);
|
|
XftFontClose(this->display, msgfont);
|
|
XftFontClose(this->display, introfont);
|
|
XftFontClose(this->display, welcomefont);
|
|
XftFontClose(this->display, enterfont);
|
|
|
|
if (mode == Mode_Lock) {
|
|
XFreeGC(this->display, this->win_gc);
|
|
}
|
|
|
|
delete image;
|
|
}
|
|
|
|
|
|
void Panel::openPanel()
|
|
{
|
|
if (image->imageLoaded()) {
|
|
/* Create window */
|
|
this->window = XCreateSimpleWindow(
|
|
this->display,
|
|
this->root_window,
|
|
X,
|
|
Y,
|
|
image->Width(),
|
|
image->Height(),
|
|
0,
|
|
this->getColor("white"),
|
|
this->getColor("white")
|
|
);
|
|
} else {
|
|
this->window = XCreateSimpleWindow(
|
|
this->display,
|
|
this->root_window,
|
|
X,
|
|
Y,
|
|
XWidthOfScreen(ScreenOfDisplay(this->display, this->screen)),
|
|
XHeightOfScreen(ScreenOfDisplay(this->display, this->screen)),
|
|
0,
|
|
this->getColor("white"),
|
|
this->getColor("white")
|
|
);
|
|
}
|
|
|
|
/* Events */
|
|
XSelectInput(this->display, this->window, ExposureMask | KeyPressMask);
|
|
|
|
/* Set background */
|
|
XSetWindowBackgroundPixmap(this->display, this->window, this->panel_pixmap);
|
|
|
|
/* Show window */
|
|
XMapWindow(this->display, this->window);
|
|
XMoveWindow(this->display, this->window, X, Y); /* override wm positioning (for tests) */
|
|
|
|
/* Grab keyboard */
|
|
XGrabKeyboard(this->display, this->window, False, GrabModeAsync, GrabModeAsync, CurrentTime);
|
|
|
|
XFlush(this->display);
|
|
}
|
|
|
|
|
|
void Panel::closePanel()
|
|
{
|
|
XUngrabKeyboard(this->display, CurrentTime);
|
|
XUnmapWindow(this->display, this->window);
|
|
XDestroyWindow(this->display, this->window);
|
|
XFlush(this->display);
|
|
}
|
|
|
|
|
|
void Panel::clearPanel()
|
|
{
|
|
session_name = "";
|
|
session_exec = "";
|
|
this->reset();
|
|
XClearWindow(this->display, this->root_window);
|
|
XClearWindow(this->display, this->window);
|
|
Cursor(SHOW);
|
|
this->showText();
|
|
XFlush(this->display);
|
|
}
|
|
|
|
void Panel::wrongPassword(int timeout)
|
|
{
|
|
std::string message;
|
|
XGlyphInfo extents;
|
|
|
|
#if 0
|
|
if (CapsLockOn)
|
|
message = this->config->getOption("passwd_feedback_capslock");
|
|
else
|
|
#endif
|
|
message = this->config->getOption("passwd_feedback_msg");
|
|
|
|
XftDraw *draw = XftDrawCreate(
|
|
this->display,
|
|
this->window,
|
|
DefaultVisual(this->display, this->screen), DefaultColormap(this->display, this->screen)
|
|
);
|
|
XftTextExtents8(
|
|
this->display,
|
|
msgfont,
|
|
reinterpret_cast<const XftChar8*>(message.c_str()),
|
|
message.length(),
|
|
&extents
|
|
);
|
|
|
|
std::string cfgX = this->config->getOption("passwd_feedback_x");
|
|
std::string cfgY = this->config->getOption("passwd_feedback_y");
|
|
int shadowXOffset = this->config->getIntOption("msg_shadow_xoffset");
|
|
int shadowYOffset = this->config->getIntOption("msg_shadow_yoffset");
|
|
int msg_x = Schlimm::Config::absolutepos(cfgX, XWidthOfScreen(ScreenOfDisplay(this->display, this->screen)), extents.width);
|
|
int msg_y = Schlimm::Config::absolutepos(cfgY, XHeightOfScreen(ScreenOfDisplay(this->display, this->screen)), extents.height);
|
|
|
|
this->onExpose();
|
|
this->slimDrawString8(draw, &msgcolor, msgfont, msg_x, msg_y, message,
|
|
&msgshadowcolor, shadowXOffset, shadowYOffset);
|
|
|
|
if (this->config->getOption("bell") == "1")
|
|
XBell(this->display, 100);
|
|
|
|
XFlush(this->display);
|
|
sleep(timeout);
|
|
this->resetPasswd();
|
|
this->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.
|
|
this->slimDrawString8(draw, &msgcolor, msgfont, msg_x, msg_y, message,
|
|
&msgshadowcolor, shadowXOffset, shadowYOffset);
|
|
XSync(this->display, True);
|
|
XftDrawDestroy(draw);
|
|
}
|
|
|
|
void Panel::message(const std::string& text)
|
|
{
|
|
std::string cfgX, cfgY;
|
|
XGlyphInfo extents;
|
|
XftDraw *draw;
|
|
|
|
if (mode == Mode_Lock)
|
|
draw = XftDrawCreate(this->display, this->window,
|
|
DefaultVisual(this->display, this->screen), DefaultColormap(this->display, this->screen));
|
|
else
|
|
draw = XftDrawCreate(this->display, this->root_window,
|
|
DefaultVisual(this->display, this->screen), DefaultColormap(this->display, this->screen));
|
|
|
|
XftTextExtents8(this->display, msgfont,
|
|
reinterpret_cast<const XftChar8*>(text.c_str()),
|
|
text.length(), &extents);
|
|
cfgX = this->config->getOption("msg_x");
|
|
cfgY = this->config->getOption("msg_y");
|
|
int shadowXOffset = this->config->getIntOption("msg_shadow_xoffset");
|
|
int shadowYOffset = this->config->getIntOption("msg_shadow_yoffset");
|
|
int msg_x, msg_y;
|
|
|
|
if (mode == Mode_Lock) {
|
|
msg_x = Schlimm::Config::absolutepos(cfgX, viewport.width, extents.width);
|
|
msg_y = Schlimm::Config::absolutepos(cfgY, viewport.height, extents.height);
|
|
} else {
|
|
msg_x = Schlimm::Config::absolutepos(cfgX, XWidthOfScreen(ScreenOfDisplay(this->display, this->screen)), extents.width);
|
|
msg_y = Schlimm::Config::absolutepos(cfgY, XHeightOfScreen(ScreenOfDisplay(this->display, this->screen)), extents.height);
|
|
}
|
|
|
|
this->slimDrawString8 (draw, &msgcolor, msgfont, msg_x, msg_y,
|
|
text,
|
|
&msgshadowcolor,
|
|
shadowXOffset, shadowYOffset);
|
|
XFlush(this->display);
|
|
XftDrawDestroy(draw);
|
|
}
|
|
|
|
|
|
void Panel::error(const std::string& text)
|
|
{
|
|
this->closePanel();
|
|
this->message(text);
|
|
sleep(ERROR_DURATION);
|
|
this->openPanel();
|
|
this->clearPanel();
|
|
}
|
|
|
|
unsigned long Panel::getColor(const char* colorname)
|
|
{
|
|
XColor color;
|
|
XWindowAttributes attributes;
|
|
|
|
if (mode == Mode_Lock) {
|
|
XGetWindowAttributes(this->display, this->window, &attributes);
|
|
} else {
|
|
XGetWindowAttributes(this->display, this->root_window, &attributes);
|
|
}
|
|
|
|
color.pixel = 0;
|
|
|
|
if (!XParseColor(this->display, attributes.colormap, colorname, &color)) {
|
|
Log::log(std::string(APPNAME) + ": can't parse color " + colorname);
|
|
} else if(!XAllocColor(this->display, attributes.colormap, &color)) {
|
|
Log::log(std::string(APPNAME) + ": can't allocate color " + colorname);
|
|
}
|
|
|
|
return color.pixel;
|
|
}
|
|
|
|
|
|
void Panel::cursor(int visible)
|
|
{
|
|
const char* text = NULL;
|
|
int xx = 0, yy = 0, y2 = 0, cheight = 0;
|
|
const char* txth = "Wj"; /* used to get cursor height */
|
|
|
|
if (mode == Mode_Lock) {
|
|
text = HiddenPasswdBuffer.c_str();
|
|
xx = input_pass_x;
|
|
yy = input_pass_y;
|
|
} else {
|
|
switch(field) {
|
|
case Get_Passwd:
|
|
text = HiddenPasswdBuffer.c_str();
|
|
xx = input_pass_x;
|
|
yy = input_pass_y;
|
|
break;
|
|
|
|
case Get_Name:
|
|
text = NameBuffer.c_str();
|
|
xx = input_name_x;
|
|
yy = input_name_y;
|
|
break;
|
|
}
|
|
}
|
|
|
|
XGlyphInfo extents;
|
|
XftTextExtents8(this->display, font, (XftChar8*)txth, strlen(txth), &extents);
|
|
cheight = extents.height;
|
|
y2 = yy - extents.y + extents.height;
|
|
XftTextExtents8(this->display, font, (XftChar8*)text, strlen(text), &extents);
|
|
xx += extents.width;
|
|
|
|
if(visible == SHOW) {
|
|
if (mode == Mode_Lock) {
|
|
xx += viewport.x;
|
|
yy += viewport.y;
|
|
y2 += viewport.y;
|
|
}
|
|
XSetForeground(this->display, this->text_gc,
|
|
this->getColor(this->config->getOption("input_color").c_str()));
|
|
|
|
XDrawLine(this->display, this->window, this->text_gc,
|
|
xx+1, yy-cheight,
|
|
xx+1, y2);
|
|
} else {
|
|
if (mode == Mode_Lock)
|
|
this->applyBackground(Rectangle(xx+1, yy-cheight,
|
|
1, y2-(yy-cheight)+1));
|
|
else
|
|
XClearArea(this->display, this->window, xx+1, yy-cheight,
|
|
1, y2-(yy-cheight)+1, false);
|
|
}
|
|
}
|
|
|
|
|
|
void Panel::eventHandler(const Panel::FieldType& curfield)
|
|
{
|
|
XEvent event;
|
|
field = curfield;
|
|
bool loop = true;
|
|
|
|
if (mode == Mode_DM)
|
|
this->onExpose();
|
|
|
|
struct pollfd x11_pfd = {0};
|
|
x11_pfd.fd = ConnectionNumber(this->display);
|
|
x11_pfd.events = POLLIN;
|
|
|
|
while (loop) {
|
|
if (XPending(this->display) || poll(&x11_pfd, 1, -1) > 0) {
|
|
while(XPending(this->display)) {
|
|
XNextEvent(this->display, &event);
|
|
switch(event.type) {
|
|
case Expose:
|
|
this->onExpose();
|
|
break;
|
|
|
|
case KeyPress:
|
|
loop = this->onKeyPress(event);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void Panel::onExpose(void)
|
|
{
|
|
XftDraw *draw = XftDrawCreate(this->display, this->window,
|
|
DefaultVisual(this->display, this->screen), DefaultColormap(this->display, this->screen));
|
|
|
|
if (mode == Mode_Lock)
|
|
this->applyBackground();
|
|
else
|
|
XClearWindow(this->display, this->window);
|
|
|
|
if (input_pass_x != input_name_x || input_pass_y != input_name_y){
|
|
this->slimDrawString8 (draw, &inputcolor, font, input_name_x, input_name_y,
|
|
NameBuffer,
|
|
&inputshadowcolor,
|
|
inputShadowXOffset, inputShadowYOffset);
|
|
this->slimDrawString8 (draw, &inputcolor, font, input_pass_x, input_pass_y,
|
|
HiddenPasswdBuffer,
|
|
&inputshadowcolor,
|
|
inputShadowXOffset, inputShadowYOffset);
|
|
} else { /*single input mode */
|
|
switch(field) {
|
|
case Get_Passwd:
|
|
this->slimDrawString8 (draw, &inputcolor, font,
|
|
input_pass_x, input_pass_y,
|
|
HiddenPasswdBuffer,
|
|
&inputshadowcolor,
|
|
inputShadowXOffset, inputShadowYOffset);
|
|
break;
|
|
case Get_Name:
|
|
this->slimDrawString8 (draw, &inputcolor, font,
|
|
input_name_x, input_name_y,
|
|
NameBuffer,
|
|
&inputshadowcolor,
|
|
inputShadowXOffset, inputShadowYOffset);
|
|
break;
|
|
}
|
|
}
|
|
|
|
XftDrawDestroy (draw);
|
|
Cursor(SHOW);
|
|
this->showText();
|
|
}
|
|
|
|
|
|
void Panel::eraseLastChar(std::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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool Panel::onKeyPress(XEvent& event)
|
|
{
|
|
char ascii;
|
|
KeySym keysym;
|
|
XComposeStatus compstatus;
|
|
int xx = 0;
|
|
int yy = 0;
|
|
std::string text;
|
|
std::string formerString = "";
|
|
|
|
XLookupString(&event.xkey, &ascii, 1, &keysym, &compstatus);
|
|
switch(keysym){
|
|
case XK_F1:
|
|
this->switchSession();
|
|
return true;
|
|
|
|
case XK_F11:
|
|
/* Take a screenshot */
|
|
system(this->config->getOption("screenshot_cmd").c_str());
|
|
return true;
|
|
|
|
case XK_Return:
|
|
case XK_KP_Enter:
|
|
if (field==Get_Name){
|
|
/* Don't allow an empty username */
|
|
if (NameBuffer.empty()) return true;
|
|
|
|
if (NameBuffer==CONSOLE_STR){
|
|
action = Console;
|
|
} else if (NameBuffer==HALT_STR){
|
|
action = Halt;
|
|
} else if (NameBuffer==REBOOT_STR){
|
|
action = Reboot;
|
|
} else if (NameBuffer==SUSPEND_STR){
|
|
action = Suspend;
|
|
} else if (NameBuffer==EXIT_STR){
|
|
action = Exit;
|
|
} else{
|
|
if (mode == Mode_DM)
|
|
action = Login;
|
|
else
|
|
action = Lock;
|
|
}
|
|
};
|
|
return false;
|
|
default:
|
|
break;
|
|
};
|
|
|
|
Cursor(HIDE);
|
|
switch(keysym){
|
|
case XK_Delete:
|
|
case XK_BackSpace:
|
|
this->eraseLastChar(formerString);
|
|
break;
|
|
|
|
case XK_w:
|
|
case XK_u:
|
|
if (reinterpret_cast<XKeyEvent&>(event).state & ControlMask) {
|
|
switch(field) {
|
|
case Get_Passwd:
|
|
formerString = HiddenPasswdBuffer;
|
|
HiddenPasswdBuffer.clear();
|
|
PasswdBuffer.clear();
|
|
break;
|
|
case Get_Name:
|
|
formerString = NameBuffer;
|
|
NameBuffer.clear();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case XK_h:
|
|
if (reinterpret_cast<XKeyEvent&>(event).state & ControlMask) {
|
|
this->eraseLastChar(formerString);
|
|
break;
|
|
}
|
|
/* Deliberate fall-through */
|
|
|
|
default:
|
|
if (isprint(ascii) && (keysym < XK_Shift_L || keysym > XK_Hyper_R)){
|
|
switch(field) {
|
|
case GET_NAME:
|
|
formerString=NameBuffer;
|
|
if (NameBuffer.length() < INPUT_MAXLENGTH_NAME-1){
|
|
NameBuffer.append(&ascii,1);
|
|
};
|
|
break;
|
|
case GET_PASSWD:
|
|
formerString=HiddenPasswdBuffer;
|
|
if (PasswdBuffer.length() < INPUT_MAXLENGTH_PASSWD-1){
|
|
PasswdBuffer.append(&ascii,1);
|
|
HiddenPasswdBuffer.append("*");
|
|
};
|
|
break;
|
|
};
|
|
}
|
|
else {
|
|
return true; //nodraw if notchange
|
|
};
|
|
break;
|
|
};
|
|
|
|
XGlyphInfo extents;
|
|
XftDraw *draw = XftDrawCreate(this->display, this->window,
|
|
DefaultVisual(this->display, this->screen), DefaultColormap(this->display, this->screen));
|
|
|
|
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()){
|
|
const char* txth = "Wj"; /* get proper maximum height ? */
|
|
XftTextExtents8(this->display, font,
|
|
reinterpret_cast<const XftChar8*>(txth), strlen(txth), &extents);
|
|
int maxHeight = extents.height;
|
|
|
|
XftTextExtents8(this->display, font,
|
|
reinterpret_cast<const XftChar8*>(formerString.c_str()),
|
|
formerString.length(), &extents);
|
|
int maxLength = extents.width;
|
|
|
|
if (mode == Mode_Lock)
|
|
this->applyBackground(Rectangle(input_pass_x - 3,
|
|
input_pass_y - maxHeight - 3,
|
|
maxLength + 6, maxHeight + 6));
|
|
else
|
|
XClearArea(this->display, this->window, xx - 3, yy-maxHeight - 3,
|
|
maxLength + 6, maxHeight + 6, false);
|
|
}
|
|
|
|
if (!text.empty()) {
|
|
this->slimDrawString8 (draw, &inputcolor, font, xx, yy,
|
|
text,
|
|
&inputshadowcolor,
|
|
inputShadowXOffset, inputShadowYOffset);
|
|
}
|
|
|
|
XftDrawDestroy (draw);
|
|
Cursor(SHOW);
|
|
return true;
|
|
}
|
|
|
|
|
|
/* Draw welcome and "enter username" message */
|
|
void Panel::showText()
|
|
{
|
|
std::string cfgX, cfgY;
|
|
XGlyphInfo extents;
|
|
|
|
bool singleInputMode =
|
|
input_name_x == input_pass_x &&
|
|
input_name_y == input_pass_y;
|
|
|
|
XftDraw *draw = XftDrawCreate(this->display, this->window,
|
|
DefaultVisual(this->display, this->screen), DefaultColormap(this->display, this->screen));
|
|
/* welcome message */
|
|
XftTextExtents8(this->display, welcomefont, (XftChar8*)welcome_message.c_str(),
|
|
strlen(welcome_message.c_str()), &extents);
|
|
cfgX = this->config->getOption("welcome_x");
|
|
cfgY = this->config->getOption("welcome_y");
|
|
int shadowXOffset = this->config->getIntOption("welcome_shadow_xoffset");
|
|
int shadowYOffset = this->config->getIntOption("welcome_shadow_yoffset");
|
|
|
|
if (image->imageLoaded()) {
|
|
welcome_x = Schlimm::Config::absolutepos(cfgX, image->Width(), extents.width);
|
|
welcome_y = Schlimm::Config::absolutepos(cfgY, image->Height(), extents.height);
|
|
} else {
|
|
//Use somewhere in the middle of the screen.
|
|
welcome_x = Schlimm::Config::absolutepos(cfgX, XWidthOfScreen(ScreenOfDisplay(this->display, this->screen)), extents.width);
|
|
welcome_y = Schlimm::Config::absolutepos(cfgY, XHeightOfScreen(ScreenOfDisplay(this->display, this->screen)), extents.height);
|
|
}
|
|
|
|
if (welcome_x >= 0 && welcome_y >= 0) {
|
|
this->slimDrawString8(
|
|
draw,
|
|
&welcomecolor,
|
|
welcomefont,
|
|
welcome_x,
|
|
welcome_y,
|
|
welcome_message,
|
|
&welcomeshadowcolor,
|
|
shadowXOffset,
|
|
shadowYOffset
|
|
);
|
|
}
|
|
|
|
/* Enter username-password message */
|
|
std::string msg;
|
|
if ((!singleInputMode|| field == Get_Passwd) && mode == Mode_DM) {
|
|
msg = this->config->getOption("password_msg");
|
|
XftTextExtents8(this->display, enterfont, (XftChar8*)msg.c_str(),
|
|
strlen(msg.c_str()), &extents);
|
|
cfgX = this->config->getOption("password_x");
|
|
cfgY = this->config->getOption("password_y");
|
|
int shadowXOffset = this->config->getIntOption("username_shadow_xoffset");
|
|
int shadowYOffset = this->config->getIntOption("username_shadow_yoffset");
|
|
if (image->imageLoaded()) {
|
|
password_x = Schlimm::Config::absolutepos(cfgX, image->Width(), extents.width);
|
|
password_y = Schlimm::Config::absolutepos(cfgY, image->Height(), extents.height);
|
|
} else {
|
|
//Use somewhere in the middle of the screen.
|
|
password_x = Schlimm::Config::absolutepos(cfgX, XWidthOfScreen(ScreenOfDisplay(this->display, this->screen)), extents.width);
|
|
password_y = Schlimm::Config::absolutepos(cfgY, XHeightOfScreen(ScreenOfDisplay(this->display, this->screen)), extents.height);
|
|
}
|
|
if (password_x >= 0 && password_y >= 0){
|
|
this->slimDrawString8(
|
|
draw,
|
|
&entercolor,
|
|
enterfont,
|
|
password_x,
|
|
password_y,
|
|
msg,
|
|
&entershadowcolor,
|
|
shadowXOffset,
|
|
shadowYOffset
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!singleInputMode || (field == Get_Name)) {
|
|
msg = this->config->getOption("username_msg");
|
|
XftTextExtents8(
|
|
this->display,
|
|
enterfont,
|
|
(XftChar8*)msg.c_str(),
|
|
strlen(msg.c_str()),
|
|
&extents
|
|
);
|
|
cfgX = this->config->getOption("username_x");
|
|
cfgY = this->config->getOption("username_y");
|
|
int shadowXOffset = this->config->getIntOption("username_shadow_xoffset");
|
|
int shadowYOffset = this->config->getIntOption("username_shadow_yoffset");
|
|
if (image->imageLoaded()) {
|
|
username_x = Schlimm::Config::absolutepos(cfgX, image->Width(), extents.width);
|
|
username_y = Schlimm::Config::absolutepos(cfgY, image->Height(), extents.height);
|
|
} else {
|
|
username_x = Schlimm::Config::absolutepos(cfgX, viewport.width, extents.width);
|
|
username_y = Schlimm::Config::absolutepos(cfgY, viewport.height, extents.height);
|
|
}
|
|
if (username_x >= 0 && username_y >= 0){
|
|
this->slimDrawString8(
|
|
draw,
|
|
&entercolor,
|
|
enterfont,
|
|
username_x,
|
|
username_y,
|
|
msg,
|
|
&entershadowcolor,
|
|
shadowXOffset,
|
|
shadowYOffset
|
|
);
|
|
}
|
|
}
|
|
XftDrawDestroy(draw);
|
|
|
|
if (mode == Mode_Lock) {
|
|
// If only the password box is visible, draw the user name somewhere too
|
|
std::string user_msg = fmt::format("User: {}", this->getName());
|
|
int show_username = this->config->getIntOption("show_username");
|
|
if (singleInputMode && show_username) {
|
|
this->message(user_msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
std::string Panel::getSession()
|
|
{
|
|
return session_exec;
|
|
}
|
|
|
|
|
|
/* choose next available session type */
|
|
void Panel::switchSession()
|
|
{
|
|
if (this->config->hasSessions()) {
|
|
std::pair<std::string, std::string> ses = this->config->nextSession();
|
|
session_name = ses.first;
|
|
session_exec = ses.second;
|
|
if (session_name.size() > 0) {
|
|
this->showSession();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Display session type on the screen */
|
|
void Panel::showSession()
|
|
{
|
|
std::string msg_x, msg_y;
|
|
XClearWindow(this->display, this->root_window);
|
|
std::string currsession = this->config->getOption("session_msg") + " " + session_name;
|
|
XGlyphInfo extents;
|
|
|
|
sessionfont = XftFontOpenName(this->display, this->screen, this->config->getOption("session_font").c_str());
|
|
|
|
XftDraw *draw = XftDrawCreate(this->display, this->root_window,
|
|
DefaultVisual(this->display, this->screen), DefaultColormap(this->display, this->screen));
|
|
XftTextExtents8(this->display, sessionfont, reinterpret_cast<const XftChar8*>(currsession.c_str()),
|
|
currsession.length(), &extents);
|
|
msg_x = this->config->getOption("session_x");
|
|
msg_y = this->config->getOption("session_y");
|
|
int x = Schlimm::Config::absolutepos(msg_x, XWidthOfScreen(ScreenOfDisplay(this->display, this->screen)), extents.width);
|
|
int y = Schlimm::Config::absolutepos(msg_y, XHeightOfScreen(ScreenOfDisplay(this->display, this->screen)), extents.height);
|
|
int shadowXOffset = this->config->getIntOption("session_shadow_xoffset");
|
|
int shadowYOffset = this->config->getIntOption("session_shadow_yoffset");
|
|
|
|
this->slimDrawString8(draw, &sessioncolor, sessionfont, x, y,
|
|
currsession,
|
|
&sessionshadowcolor,
|
|
shadowXOffset, shadowYOffset);
|
|
XFlush(this->display);
|
|
XftDrawDestroy(draw);
|
|
}
|
|
|
|
|
|
void Panel::slimDrawString8(
|
|
XftDraw *d,
|
|
XftColor *color,
|
|
XftFont *font,
|
|
int x,
|
|
int y,
|
|
const std::string& str,
|
|
XftColor* shadowColor,
|
|
int xOffset,
|
|
int yOffset
|
|
)
|
|
{
|
|
int calc_x = 0;
|
|
int calc_y = 0;
|
|
if (mode == Mode_Lock) {
|
|
calc_x = viewport.x;
|
|
calc_y = viewport.y;
|
|
}
|
|
|
|
if (xOffset && yOffset) {
|
|
XftDrawStringUtf8(d, shadowColor, font,
|
|
x + xOffset + calc_x,
|
|
y + yOffset + calc_y,
|
|
reinterpret_cast<const FcChar8*>(str.c_str()),
|
|
str.length());
|
|
}
|
|
|
|
XftDrawStringUtf8(d, color, font,
|
|
x + calc_x,
|
|
y + calc_y,
|
|
reinterpret_cast<const FcChar8*>(str.c_str()),
|
|
str.length());
|
|
}
|
|
|
|
|
|
Panel::ActionType Panel::getAction(void) const
|
|
{
|
|
return action;
|
|
}
|
|
|
|
|
|
void Panel::reset(void)
|
|
{
|
|
this->resetName();
|
|
this->resetPasswd();
|
|
}
|
|
|
|
|
|
void Panel::resetName(void)
|
|
{
|
|
NameBuffer.clear();
|
|
}
|
|
|
|
|
|
void Panel::resetPasswd(void)
|
|
{
|
|
PasswdBuffer.clear();
|
|
HiddenPasswdBuffer.clear();
|
|
}
|
|
|
|
|
|
void Panel::setName(const std::string& name)
|
|
{
|
|
NameBuffer=name;
|
|
if (mode == Mode_DM)
|
|
action = Login;
|
|
else
|
|
action = Lock;
|
|
}
|
|
|
|
|
|
const std::string& Panel::getName(void) const
|
|
{
|
|
return this->NameBuffer;
|
|
}
|
|
|
|
|
|
const std::string& Panel::getPasswd(void) const
|
|
{
|
|
return this->PasswdBuffer;
|
|
}
|
|
|
|
|
|
Rectangle Panel::getPrimaryViewport()
|
|
{
|
|
Rectangle fallback;
|
|
Rectangle result;
|
|
|
|
RROutput primary;
|
|
XRROutputInfo *primary_info;
|
|
XRRScreenResources *resources;
|
|
XRRCrtcInfo *crtc_info;
|
|
|
|
int crtc;
|
|
|
|
fallback.x = 0;
|
|
fallback.y = 0;
|
|
fallback.width = DisplayWidth(this->display, this->screen);
|
|
fallback.height = DisplayHeight(this->display, this->screen);
|
|
|
|
primary = XRRGetOutputPrimary(this->display, this->window);
|
|
if (!primary) {
|
|
return fallback;
|
|
}
|
|
resources = XRRGetScreenResources(this->display, this->window);
|
|
if (!resources)
|
|
return fallback;
|
|
|
|
primary_info = XRRGetOutputInfo(this->display, resources, primary);
|
|
if (!primary_info) {
|
|
XRRFreeScreenResources(resources);
|
|
return fallback;
|
|
}
|
|
|
|
// Fixes bug with multiple monitors. Just pick first monitor if
|
|
// XRRGetOutputInfo gives returns bad into for crtc.
|
|
if (primary_info->crtc < 1) {
|
|
if (primary_info->ncrtc > 0) {
|
|
crtc = primary_info->crtcs[0];
|
|
} else {
|
|
Log::log("Cannot get crtc from xrandr!");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
} else {
|
|
crtc = primary_info->crtc;
|
|
}
|
|
|
|
crtc_info = XRRGetCrtcInfo(this->display, resources, crtc);
|
|
|
|
if (!crtc_info) {
|
|
XRRFreeOutputInfo(primary_info);
|
|
XRRFreeScreenResources(resources);
|
|
return fallback;
|
|
}
|
|
|
|
result.x = crtc_info->x;
|
|
result.y = crtc_info->y;
|
|
result.width = crtc_info->width;
|
|
result.height = crtc_info->height;
|
|
|
|
XRRFreeCrtcInfo(crtc_info);
|
|
XRRFreeOutputInfo(primary_info);
|
|
XRRFreeScreenResources(resources);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
void Panel::applyBackground(Rectangle rect)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (rect.is_empty()) {
|
|
rect.x = 0;
|
|
rect.y = 0;
|
|
rect.width = viewport.width;
|
|
rect.height = viewport.height;
|
|
}
|
|
|
|
ret = XCopyArea(this->display, this->panel_pixmap, this->window, this->win_gc,
|
|
rect.x, rect.y, rect.width, rect.height,
|
|
viewport.x + rect.x, viewport.y + rect.y);
|
|
|
|
if (!ret) {
|
|
Log::log("Failed to put pixmap on the screen!");
|
|
}
|
|
}
|