blob: f06387ccfd350fe11ac04db348f02aa9abe15bac [file] [log] [blame]
/*******************************************************************************
* Copyright (C) 2010, Linaro Limited.
*
* This file is part of PowerDebug.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Amit Arora <amit.arora@linaro.org> (IBM Corporation)
* - initial API and implementation
*******************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <ncurses.h>
#include <sys/types.h>
#include <regex.h>
#include "powerdebug.h"
#include "mainloop.h"
#include "regulator.h"
#include "display.h"
enum { PT_COLOR_DEFAULT = 1,
PT_COLOR_HEADER_BAR,
PT_COLOR_ERROR,
PT_COLOR_RED,
PT_COLOR_YELLOW,
PT_COLOR_GREEN,
PT_COLOR_BRIGHT,
PT_COLOR_BLUE,
};
static WINDOW *header_win;
static WINDOW *footer_win;
static WINDOW *main_win;
static int current_win;
/* Number of lines in the virtual window */
static const int maxrows = 1024;
struct rowdata {
int attr;
void *data;
};
struct windata {
WINDOW *pad;
struct display_ops *ops;
struct rowdata *rowdata;
char *name;
int nrdata;
int scrolling;
int cursor;
};
/* Warning this is linked with the enum { CLOCK, REGULATOR, ... } */
struct windata windata[] = {
[CLOCK] = { .name = "Clocks" },
[REGULATOR] = { .name = "Regulators" },
[SENSOR] = { .name = "Sensors" },
[GPIO] = { .name = "Gpio" },
};
static void display_fini(void)
{
endwin();
}
static int display_show_header(int win)
{
int i;
int curr_pointer = 0;
size_t array_size = sizeof(windata) / sizeof(windata[0]);
wattrset(header_win, COLOR_PAIR(PT_COLOR_HEADER_BAR));
wbkgd(header_win, COLOR_PAIR(PT_COLOR_HEADER_BAR));
werase(header_win);
mvwprintw(header_win, 0, curr_pointer, "PowerDebug %s", VERSION);
curr_pointer += 20;
for (i = 0; i < array_size; i++) {
if (win == i)
wattron(header_win, A_REVERSE);
else
wattroff(header_win, A_REVERSE);
mvwprintw(header_win, 0, curr_pointer, " %s ", windata[i].name);
curr_pointer += strlen(windata[i].name) + 2;
}
wrefresh(header_win);
return 0;
}
#define footer_label " Q (Quit) R (Refresh) Other Keys: 'Left', " \
"'Right' , 'Up', 'Down', 'enter', , 'Esc'"
static int display_show_footer(int win, char *string)
{
werase(footer_win);
wattron(footer_win, A_REVERSE);
mvwprintw(footer_win, 0, 0, "%s", string ? string : footer_label);
wattroff(footer_win, A_REVERSE);
wrefresh(footer_win);
return 0;
}
static int display_refresh(int win, bool read)
{
/* we are trying to refresh a window which is not showed */
if (win != current_win)
return 0;
if (windata[win].ops && windata[win].ops->display)
return windata[win].ops->display(read);
if (werase(main_win))
return -1;
return wrefresh(main_win);
}
int display_refresh_pad(int win)
{
int maxx, maxy;
getmaxyx(stdscr, maxy, maxx);
return prefresh(windata[win].pad, windata[win].scrolling,
0, 2, 0, maxy - 2, maxx);
}
static int display_show_unselection(int win, int line, bool bold)
{
if (mvwchgat(windata[win].pad, line, 0, -1,
bold ? WA_BOLD: WA_NORMAL, 0, NULL) < 0)
return -1;
return display_refresh_pad(win);
}
void *display_get_row_data(int win)
{
return windata[win].rowdata[windata[win].cursor].data;
}
static int display_select(void)
{
if (windata[current_win].ops && windata[current_win].ops->select)
return windata[current_win].ops->select();
return 0;
}
static int display_next_panel(void)
{
size_t array_size = sizeof(windata) / sizeof(windata[0]);
current_win++;
current_win %= array_size;
return current_win;
}
static int display_prev_panel(void)
{
size_t array_size = sizeof(windata) / sizeof(windata[0]);
current_win--;
if (current_win < 0)
current_win = array_size - 1;
return current_win;
}
static int display_next_line(void)
{
int maxx, maxy;
int cursor = windata[current_win].cursor;
int nrdata = windata[current_win].nrdata;
int scrolling = windata[current_win].scrolling;
struct rowdata *rowdata = windata[current_win].rowdata;
getmaxyx(stdscr, maxy, maxx);
if (cursor >= nrdata)
return cursor;
display_show_unselection(current_win, cursor, rowdata[cursor].attr);
if (cursor < nrdata - 1) {
if (cursor >= (maxy - 4 + scrolling))
scrolling++;
cursor++;
}
windata[current_win].scrolling = scrolling;
windata[current_win].cursor = cursor;
return cursor;
}
static int display_prev_line(void)
{
int cursor = windata[current_win].cursor;
int nrdata = windata[current_win].nrdata;
int scrolling = windata[current_win].scrolling;
struct rowdata *rowdata = windata[current_win].rowdata;
if (cursor >= nrdata)
return cursor;
display_show_unselection(current_win, cursor, rowdata[cursor].attr);
if (cursor > 0) {
if (cursor <= scrolling)
scrolling--;
cursor--;
}
windata[current_win].scrolling = scrolling;
windata[current_win].cursor = cursor;
return cursor;
}
static int display_set_row_data(int win, int line, void *data, int attr)
{
struct rowdata *rowdata = windata[win].rowdata;
if (line >= windata[win].nrdata) {
rowdata = realloc(rowdata, sizeof(struct rowdata) * (line + 1));
if (!rowdata)
return -1;
windata[win].nrdata = line + 1;
}
rowdata[line].data = data;
rowdata[line].attr = attr;
windata[win].rowdata = rowdata;
return 0;
}
int display_reset_cursor(int win)
{
windata[win].nrdata = 0;
werase(windata[win].pad);
return wmove(windata[win].pad, 0, 0);
}
int display_print_line(int win, int line, char *str, int bold, void *data)
{
int attr = 0;
if (bold)
attr |= WA_BOLD;
if (line == windata[win].cursor)
attr |= WA_STANDOUT;
if (display_set_row_data(win, line, data, attr))
return -1;
if (attr)
wattron(windata[win].pad, attr);
wprintw(windata[win].pad, "%s\n", str);
if (attr)
wattroff(windata[win].pad, attr);
return 0;
}
static int display_find_keystroke(int fd, void *data);
struct find_data {
size_t len;
char *string;
regex_t *reg;
int ocursor;
int oscrolling;
};
struct find_data *display_find_init(void)
{
const char *regexp = "^[a-z|0-9|_|-|.]";
struct find_data *findd;
const size_t len = 64;
regex_t *reg;
char *search4;
int maxx, maxy;
getmaxyx(stdscr, maxy, maxx);
reg = malloc(sizeof(*reg));
if (!reg)
return NULL;
if (regcomp(reg, regexp, REG_ICASE))
goto out_free_reg;
search4 = malloc(len);
if (!search4)
goto out_free_regcomp;
memset(search4, '\0', len);
findd = malloc(sizeof(*findd));
if (!findd)
goto out_free_search4;
findd->string = search4;
findd->reg = reg;
findd->len = len;
/* save the location of the cursor on the main window in order to
* browse the search result
*/
findd->ocursor = windata[current_win].cursor;
findd->oscrolling = windata[current_win].scrolling;
windata[current_win].cursor = 0;
windata[current_win].scrolling = 0;
curs_set(1);
out:
return findd;
out_free_search4:
free(search4);
out_free_regcomp:
regfree(reg);
out_free_reg:
free(reg);
goto out;
}
static void display_find_fini(struct find_data *findd)
{
windata[current_win].cursor = findd->ocursor;
windata[current_win].scrolling = findd->oscrolling;
regfree(findd->reg);
free(findd->string);
free(findd);
curs_set(0);
}
static int display_switch_to_find(int fd)
{
struct find_data *findd;
findd = display_find_init();
if (!findd)
return -1;
if (mainloop_del(fd))
return -1;
if (mainloop_add(fd, display_find_keystroke, findd))
return -1;
if (display_show_footer(current_win, "find (esc to exit)?"))
return -1;
return 0;
}
static int display_keystroke(int fd, void *data)
{
int keystroke = getch();
switch (keystroke) {
case KEY_RIGHT:
case '\t':
display_show_header(display_next_panel());
break;
case KEY_LEFT:
case KEY_BTAB:
display_show_header(display_prev_panel());
break;
case KEY_DOWN:
display_next_line();
break;
case KEY_UP:
display_prev_line();
break;
case '\r':
display_select();
break;
case EOF:
case 'q':
case 'Q':
return 1;
case '/':
return display_switch_to_find(fd);
case 'r':
case 'R':
return display_refresh(current_win, true);
default:
return 0;
}
display_refresh(current_win, false);
return 0;
}
static int display_switch_to_main(int fd)
{
if (mainloop_del(fd))
return -1;
if (mainloop_add(fd, display_keystroke, NULL))
return -1;
if (display_show_header(current_win))
return -1;
if (display_show_footer(current_win, NULL))
return -1;
return display_refresh(current_win, false);
}
static int display_find_keystroke(int fd, void *data)
{
struct find_data *findd = data;
regex_t *reg = findd->reg;
char *string = findd->string;
int keystroke = getch();
char match[2] = { [0] = (char)keystroke, [1] = '\0' };
regmatch_t m[1];
switch (keystroke) {
case '\e':
display_find_fini(findd);
return display_switch_to_main(fd);
case KEY_DOWN:
display_next_line();
break;
case KEY_UP:
display_prev_line();
break;
case KEY_BACKSPACE:
if (strlen(string))
string[strlen(string) - 1] = '\0';
windata[current_win].cursor = 0;
windata[current_win].scrolling = 0;
break;
case '\r':
if (!windata[current_win].ops || !windata[current_win].ops->selectf)
return 0;
if (windata[current_win].ops->selectf())
return -1;
windata[current_win].cursor = 0;
windata[current_win].scrolling = 0;
return 0;
default:
/* We don't want invalid characters for a name */
if (regexec(reg, match, 1, m, 0))
return 0;
if (strlen(string) < findd->len - 1)
string[strlen(string)] = (char)keystroke;
windata[current_win].cursor = 0;
windata[current_win].scrolling = 0;
break;
}
if (!windata[current_win].ops || !windata[current_win].ops->find)
return 0;
if (windata[current_win].ops->find(string))
return -1;
if (display_show_header(current_win))
return -1;
if (display_show_footer(current_win, strlen(string) ? string :
"find (esc to exit)?"))
return -1;
return 0;
}
int display_init(int wdefault)
{
int i, maxx, maxy;
size_t array_size = sizeof(windata) / sizeof(windata[0]);
current_win = wdefault;
if (mainloop_add(0, display_keystroke, NULL))
return -1;
if (!initscr())
return -1;
start_color();
use_default_colors();
keypad(stdscr, TRUE);
noecho();
cbreak();
curs_set(0);
nonl();
if (init_pair(PT_COLOR_DEFAULT, COLOR_WHITE, COLOR_BLACK) ||
init_pair(PT_COLOR_ERROR, COLOR_BLACK, COLOR_RED) ||
init_pair(PT_COLOR_HEADER_BAR, COLOR_WHITE, COLOR_BLACK) ||
init_pair(PT_COLOR_YELLOW, COLOR_WHITE, COLOR_YELLOW) ||
init_pair(PT_COLOR_GREEN, COLOR_WHITE, COLOR_GREEN) ||
init_pair(PT_COLOR_BRIGHT, COLOR_WHITE, COLOR_BLACK) ||
init_pair(PT_COLOR_BLUE, COLOR_WHITE, COLOR_BLUE) ||
init_pair(PT_COLOR_RED, COLOR_WHITE, COLOR_RED))
return -1;
if (atexit(display_fini))
return -1;
getmaxyx(stdscr, maxy, maxx);
for (i = 0; i < array_size; i++) {
main_win = subwin(stdscr, maxy - 2, maxx, 1, 0);
if (!main_win)
return -1;
windata[i].pad = newpad(maxrows, maxx);
if (!windata[i].pad)
return -1;
}
header_win = subwin(stdscr, 1, maxx, 0, 0);
if (!header_win)
return -1;
footer_win = subwin(stdscr, 1, maxx, maxy-1, 0);
if (!footer_win)
return -1;
if (display_show_header(wdefault))
return -1;
if (display_show_footer(wdefault, NULL))
return -1;
return display_refresh(wdefault, true);
}
int display_column_name(const char *line)
{
werase(main_win);
wattron(main_win, A_BOLD);
mvwprintw(main_win, 0, 0, "%s", line);
wattroff(main_win, A_BOLD);
wrefresh(main_win);
return 0;
}
int display_register(int win, struct display_ops *ops)
{
size_t array_size = sizeof(windata) / sizeof(windata[0]);
if (win < 0 || win >= array_size)
return -1;
windata[win].ops = ops;
return 0;
}