#include #include #include #include #include #include #include #include #include #include #include #include #include "ui.h" #include "ui_ani.h" #include "ui_input.h" #include "ui_statusbar.h" #include "ui_nwindow.h" #include "status.h" #include "config.h" #define APP_TIMEOUT 60 #define APP_TIMEOUT_FMT "%02d" #define PASSWD_WIDTH 35 #define PASSWD_HEIGHT 5 #define PASSWD_XRELPOS (unsigned int)(PASSWD_WIDTH / 2) - (PASSWD_WIDTH / 6) #define PASSWD_YRELPOS (unsigned int)(PASSWD_HEIGHT / 2) + 1 #define STRLEN(s) (sizeof(s)/sizeof(s[0])) static int ffd; static unsigned int max_x, max_y; static WINDOW *wnd_main; static struct nask_ui *nui = NULL; static pthread_t thrd; static bool active, passwd_from_ui; static unsigned int atmout = APP_TIMEOUT; static pthread_cond_t cnd_update = PTHREAD_COND_INITIALIZER; static pthread_mutex_t mtx_update = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t mtx_busy = PTHREAD_MUTEX_INITIALIZER; static sem_t sem_rdy; void register_ui_elt(ui_callback uicb, void *data, WINDOW *wnd) { struct nask_ui *tmp, *new; if (nui != NULL) { tmp = nui; while (tmp->next != NULL) { tmp = tmp->next; } } new = calloc(1, sizeof(struct nask_ui)); new->ui_elt_cb = uicb; new->wnd = wnd; new->data = data; new->next = NULL; if (nui == NULL) { nui = new; nui->next = NULL; } else { tmp->next = new; } } void unregister_ui_elt(void *data) { struct nask_ui *cur = nui, *next, *before = NULL; while (cur != NULL) { next = cur->next; if (cur->data != NULL && cur->data == data) { free(cur); if (before != NULL) { before->next = next; } else { nui = next; } } before = cur; cur = next; } } static int do_ui_update(bool timed_out) { int retval = UICB_OK; int curx = getcurx(wnd_main); int cury = getcury(wnd_main); struct nask_ui *cur = nui; /* call all draw callback's */ erase(); while (cur != NULL) { if (cur->ui_elt_cb != NULL) { cur->ui_elt_cb(cur->wnd, cur->data, timed_out); doupdate(); } else { retval = UICB_ERR_CB; } cur = cur->next; } /* TODO: Maybe export to an extra module? */ attron(COLOR_PAIR(1)); mvprintw(0, max_x - STRLEN(APP_TIMEOUT_FMT), "[" APP_TIMEOUT_FMT "]", atmout); attroff(COLOR_PAIR(1)); /* EoT (End of Todo) */ wmove(wnd_main, cury, curx); wrefresh(wnd_main); return (retval); } static void * ui_thrd(void *arg) { int cnd_ret; struct timeval now; struct timespec wait; pthread_mutex_lock(&mtx_update); gettimeofday(&now, NULL); wait.tv_sec = now.tv_sec + UILOOP_TIMEOUT; wait.tv_nsec = now.tv_usec * 1000; do_ui_update(true); sem_post(&sem_rdy); while (active == true) { pthread_mutex_unlock(&mtx_busy); cnd_ret = pthread_cond_timedwait(&cnd_update, &mtx_update, &wait); if (cnd_ret == ETIMEDOUT) { wait.tv_sec += UILOOP_TIMEOUT; } pthread_mutex_lock(&mtx_busy); if (--atmout == 0) active = false; if (active == false) { break; } do_ui_update( (cnd_ret == ETIMEDOUT ? true : false) ); } pthread_mutex_unlock(&mtx_busy); pthread_mutex_unlock(&mtx_update); return (NULL); } void ui_thrd_force_update(void) { pthread_cond_signal(&cnd_update); } WINDOW * init_ui(void) { wnd_main = initscr(); max_x = getmaxx(wnd_main); max_y = getmaxy(wnd_main); start_color(); init_pair(1, COLOR_RED, COLOR_WHITE); init_pair(2, COLOR_WHITE, COLOR_BLACK); init_pair(3, COLOR_BLACK, COLOR_WHITE); raw(); keypad(stdscr, TRUE); noecho(); cbreak(); return (wnd_main); } void free_ui(void) { delwin(wnd_main); endwin(); clear(); printf(" \033[2J\n"); } static int run_ui_thrd(void) { pthread_mutex_lock(&mtx_busy); active = true; passwd_from_ui = false; pthread_cond_signal(&cnd_update); pthread_mutex_unlock(&mtx_busy); return (pthread_create(&thrd, NULL, &ui_thrd, NULL)); } void stop_ui(void) { pthread_mutex_lock(&mtx_busy); active = false; pthread_mutex_unlock(&mtx_busy); } static int stop_ui_thrd(void) { stop_ui(); return (pthread_join(thrd, NULL)); } static int send_passwd(int fifo_fd, char *passwd, size_t len) { if (write(fifo_fd, passwd, len) != len) { memset(passwd, '\0', len); return (errno); } else { memset(passwd, '\0', len); return (0); } } static bool process_key(char key, struct input *a, WINDOW *win) { bool retval = true; atmout = APP_TIMEOUT; switch (key) { case UIKEY_ENTER: send_passwd(ffd, a->input, a->input_len); passwd_from_ui = true; retval = false; break; case UIKEY_BACKSPACE: del_input(win, a); break; case UIKEY_ESC: retval = false; ui_thrd_force_update(); break; case UIKEY_DOWN: case UIKEY_UP: case UIKEY_LEFT: case UIKEY_RIGHT: break; default: add_input(win, a, key); } return (retval); } static int lower_statusbar_update(WINDOW *win, struct statusbar *bar) { char *tmp = get_system_stat(); set_statusbar_text(bar, tmp); free(tmp); return (0); } static int infownd_update(WINDOW *win, struct txtwindow *tw) { return (0); } int do_ui(int fifo_fd) { struct input *pw_input; struct anic *heartbeat; struct statusbar *higher, *lower; struct txtwindow *infownd; char key = '\0'; char *title; asprintf(&title, "/* %s-%s */", PKGNAME, VERSION); ffd = fifo_fd; if (sem_init(&sem_rdy, 0, 0) == -1) { perror("init semaphore"); goto error; } init_ui(); pw_input = init_input((unsigned int)(max_x / 2)-PASSWD_XRELPOS, (unsigned int)(max_y / 2)-PASSWD_YRELPOS, PASSWD_WIDTH, "PASSWORD: ", MAX_PASSWD_LEN, COLOR_PAIR(3), COLOR_PAIR(2)); heartbeat = init_anic(0, 0, A_BOLD | COLOR_PAIR(1), "[%c]"); higher = init_statusbar(0, max_x, A_BOLD | COLOR_PAIR(3), NULL); lower = init_statusbar(max_y - 1, max_x, COLOR_PAIR(3), lower_statusbar_update); infownd = init_txtwindow(10, 10, 25, 8, COLOR_PAIR(3), infownd_update); register_input(NULL, pw_input); register_statusbar(higher); register_statusbar(lower); register_anic(heartbeat); activate_input(wnd_main, pw_input); set_statusbar_text(higher, title); if (run_ui_thrd() != 0) { goto error; } sem_wait(&sem_rdy); wtimeout(wnd_main, 1000); while (active == true) { if ((key = wgetch(wnd_main)) == '\0') { break; } if (key == -1) { continue; } pthread_mutex_lock(&mtx_busy); active = process_key(key, pw_input, wnd_main); activate_input(wnd_main, pw_input); do_ui_update(false); pthread_mutex_unlock(&mtx_busy); } stop_ui_thrd(); unregister_ui_elt(lower); unregister_ui_elt(higher); unregister_ui_elt(heartbeat); unregister_ui_elt(pw_input); free_input(pw_input); free_anic(heartbeat); free_statusbar(higher); free_statusbar(lower); free_txtwindow(infownd); free_ui(); return (DOUI_OK); error: free(title); return (DOUI_ERR); } bool is_passwd_from_ui(void) { bool ret; pthread_mutex_lock(&mtx_busy); ret = passwd_from_ui; pthread_mutex_unlock(&mtx_busy); return (ret); }