// This file is part of the loginx project
//
// Copyright (c) 2013 by Mike Sharov <msharov@users.sourceforge.net>
// This file is free software, distributed under the ISC license.
#include "defs.h"
#include <ncurses.h>
#include <ctype.h>
#ifndef COLOR_DEFAULT
#define COLOR_DEFAULT -1
#endif
#define min(a,b) ((a)<(b)?(a):(b))
enum {
MAX_PROMPT_WIDTH = sizeof(PASSWORD_PROMPT)-1,
MAX_INPUT_WIDTH = sizeof(PASSWORD_MASKSTR),
LOGIN_WINDOW_WIDTH = 1+1+MAX_PROMPT_WIDTH+1+MAX_INPUT_WIDTH+1+1,
LOGIN_WINDOW_HEIGHT = 1+2+1,
MSG_WINDOW_HEIGHT = 8
};
enum {
colorpair_None,
colorpair_StaticText,
colorpair_Input,
colorpair_Error
};
//----------------------------------------------------------------------
static WINDOW* _loginbox = NULL;
static WINDOW* _msgboxframe = NULL;
static WINDOW* _msgbox = NULL;
//----------------------------------------------------------------------
static void CursesInit (void)
{
static bool s_initialized = false;
if (s_initialized)
return;
if (!initscr())
ExitWithMessage ("failed to initialize curses");
atexit (CursesCleanup);
start_color();
use_default_colors();
init_pair (colorpair_StaticText, COLOR_GREEN, COLOR_DEFAULT);
init_pair (colorpair_Input, COLOR_CYAN, COLOR_DEFAULT);
init_pair (colorpair_Error, COLOR_RED, COLOR_DEFAULT);
noecho();
s_initialized = true;
}
void CursesCleanup (void)
{
if (isendwin())
return;
#define CLDWIN(w)\
if (w) {\
wclear (w);\
wnoutrefresh (w);\
delwin (w);\
w = NULL;\
}
CLDWIN(_loginbox)
CLDWIN(_msgbox)
CLDWIN(_msgboxframe)
#undef CLDWIN
wclear (stdscr);
wnoutrefresh (stdscr);
doupdate();
endwin();
}
void LoginBox (char* password, size_t passwordsz)
{
CursesInit();
if (!_loginbox) {
_loginbox = newwin (LOGIN_WINDOW_HEIGHT, LOGIN_WINDOW_WIDTH, (LINES-LOGIN_WINDOW_HEIGHT)/2, (COLS-LOGIN_WINDOW_WIDTH)/2);
if (!_loginbox)
ExitWithMessage ("failed to create login window");
keypad (_loginbox, true);
nodelay (_loginbox, false);
}
int key;
unsigned pwlen = 0;
acclist_t al = ReadAccounts();
const unsigned aln = NAccounts();
unsigned ali = SelectedAccount();
if (!aln || ali >= aln)
ExitWithMessage ("no usable accounts found");
memset (password, 0, passwordsz);
do {
wattr_set (_loginbox, A_NORMAL, colorpair_StaticText, NULL);
werase (_loginbox);
box (_loginbox, 0, 0);
mvwaddstr (_loginbox, 1,2, USERNAME_PROMPT);
mvwaddstr (_loginbox, 2,2, PASSWORD_PROMPT);
wattr_set (_loginbox, A_NORMAL, colorpair_Input, NULL);
mvwaddnstr (_loginbox, 1,2+sizeof(USERNAME_PROMPT), al[ali]->name, MAX_INPUT_WIDTH);
mvwhline (_loginbox, 2,2+sizeof(PASSWORD_PROMPT), ' ', strlen(PASSWORD_MASKSTR)+1);
mvwaddnstr (_loginbox, 2,2+sizeof(PASSWORD_PROMPT), PASSWORD_MASKSTR, min(strlen(PASSWORD_MASKSTR),pwlen));
wrefresh (_loginbox);
key = wgetch (_loginbox);
if (isprint(key) && pwlen < passwordsz-1)
password[pwlen++] = key;
else if (key == KEY_BACKSPACE && pwlen > 0)
password[--pwlen] = 0;
else if (key == KEY_UP)
ali = (ali-1) % aln;
else if (key == KEY_DOWN || key == '\t')
ali = (ali+1) % aln;
} while (key != '\n');
SetSelectedAccount (ali);
}
void AddMessage (const char* msg, bool is_error)
{
CursesInit();
if (!_msgboxframe) {
_msgboxframe = newwin (MSG_WINDOW_HEIGHT+2, COLS, LINES-MSG_WINDOW_HEIGHT-2, 0);
if (!_msgboxframe)
return;
wattr_set (_msgboxframe, A_NORMAL, colorpair_StaticText, NULL);
box (_msgboxframe, 0, 0);
wnoutrefresh (_msgboxframe);
}
if (!_msgbox) {
_msgbox = newwin (getmaxy(_msgboxframe)-2, getmaxx(_msgboxframe)-2, getbegy(_msgboxframe)+1, getbegx(_msgboxframe)+1);
if (!_msgbox)
return;
scrollok (_msgbox, true);
}
if (is_error)
wattr_set (_msgbox, A_BOLD, colorpair_Error, NULL);
else
wattr_set (_msgbox, A_NORMAL, colorpair_StaticText, NULL);
waddstr (_msgbox, msg);
waddstr (_msgbox, "\n");
wrefresh (_msgbox);
}
void ClearScreen (void)
{
if (!isatty (STDOUT_FILENO))
return;
// Clear the screen; c resets terminal, [r resets scroll region, [H homes cursor, [J erases
#define RESET_SCREEN_CMD "\ec\e[r\e[H\e[J"
write (STDOUT_FILENO, RESET_SCREEN_CMD, sizeof(RESET_SCREEN_CMD));
}