The Code
#include <panel.h>
#include <curses.h>
#include <string.h>
enum{ RED_ON_BLACK = 1,
GREEN_ON_BLACK,
CYAN_ON_BLACK,
WINDOW_FILL_COLOR};
#define LOG_WINDOW_X_POS 2
#define LOG_WINDOW_Y_POS 2
#define LOG_WINDOW_WIDTH 40
#define LOG_WINDOW_HEIGHT 10
#define LOG_MSGS_IN_WIN (LOG_WINDOW_HEIGHT - 2)
#define MAX_LOG_MESSAGES 120
char log_messages[MAX_LOG_MESSAGES][LOG_WINDOW_WIDTH];
void win_show(WINDOW *win, char *label, int label_color, int background_color);
WINDOW* log_window;
int log_lines_stored = 0;
/* Last place we stuck some logging data
*/
int log_last_ptr = -1;
int log_offset_from_bottom = 0;
/* TODO: ASSERT if # messages storable < window height
*/
/* DO: Debug macro for mvprintw(18, 0, "---------------"); refresh(); */
typedef struct window_data_TAG
{
int size_x;
int size_y;
} window_data;
void scroll_log_page_down();
void scroll_log_page_up();
void scroll_log_window(int num_lines);
void scroll_log_page_down()
{
scroll_log_window(-1 * LOG_MSGS_IN_WIN);
}
void scroll_log_page_up()
{
scroll_log_window(LOG_MSGS_IN_WIN);
}
void scroll_log_home()
{
log_offset_from_bottom = log_lines_stored - LOG_MSGS_IN_WIN;
}
void scroll_log_end()
{
log_offset_from_bottom = 0;
}
void scroll_log_window(int num_lines)
{
if (log_lines_stored <= LOG_MSGS_IN_WIN)
{
return;
}
else
{
log_offset_from_bottom += num_lines;
/* At the top?
*/
if (log_lines_stored - log_offset_from_bottom <= LOG_MSGS_IN_WIN)
{
log_offset_from_bottom = log_lines_stored - LOG_MSGS_IN_WIN;
}
/* At the bottom?
*/
else if (log_offset_from_bottom < 0)
{
log_offset_from_bottom = 0;
}
}
}
void render_log_window()
{
int screen_ptr;
int x, y;
int thumb_offset;
int thumb_width;
int data_ptr;
/* Clear the screen
*/
box(log_window, 0, 0);
for (y = 1; y < LOG_WINDOW_HEIGHT - 1; y++)
{
for (x = 1; x < LOG_WINDOW_WIDTH - 1; x++)
{
mvwaddch(log_window, y, x, ' ');
}
}
/* Is there anything to render ?
*/
if (log_last_ptr == -1)
{
return;
}
/* We draw from newest to oldest, so figure out where we should
* start - do we have enough lines for a complete screen of msgs?
*/
if (log_lines_stored > LOG_MSGS_IN_WIN)
{
screen_ptr = LOG_MSGS_IN_WIN;
data_ptr = log_last_ptr - log_offset_from_bottom;
if (data_ptr < 0)
{
data_ptr += MAX_LOG_MESSAGES;
}
/* Draw the scrollbar background
*/
for (y = 2; y < LOG_MSGS_IN_WIN; y++)
{
mvwaddch(log_window, y, LOG_WINDOW_WIDTH - 1, ACS_CKBOARD);
}
/* Draw the arrows
*/
wattron(log_window, COLOR_PAIR(GREEN_ON_BLACK));
mvwaddch(log_window, 1, LOG_WINDOW_WIDTH - 1, ACS_UARROW);
mvwaddch(log_window, LOG_WINDOW_HEIGHT - 2, LOG_WINDOW_WIDTH - 1, ACS_DARROW);
wattroff(log_window, COLOR_PAIR(GREEN_ON_BLACK));
/* Draw the 'thumb'. The thumb size is == the percentage
* of the visible screen
*/
thumb_width = (LOG_WINDOW_HEIGHT - 4) * LOG_MSGS_IN_WIN
/ log_lines_stored;
if (thumb_width <= 0)
{ thumb_width = 1;
}
thumb_offset = log_offset_from_bottom *
(LOG_WINDOW_HEIGHT - 4 - thumb_width) /
(log_lines_stored - LOG_MSGS_IN_WIN);
wattron(log_window, COLOR_PAIR(RED_ON_BLACK));
for (y = thumb_width; y > 0; y--)
{
mvwaddch(log_window, LOG_MSGS_IN_WIN - y - thumb_offset,
LOG_WINDOW_WIDTH - 1, ACS_BLOCK);
}
wattroff(log_window, COLOR_PAIR(RED_ON_BLACK));
}
else
{
screen_ptr = log_lines_stored;
data_ptr = log_last_ptr;
}
wattron(log_window, COLOR_PAIR(CYAN_ON_BLACK));
while (screen_ptr)
{
mvwprintw(log_window, screen_ptr, 1, log_messages[data_ptr]);
data_ptr--;
if (data_ptr < 0)
{
data_ptr = MAX_LOG_MESSAGES - 1;
}
screen_ptr--;
}
wattroff(log_window, COLOR_PAIR(CYAN_ON_BLACK));
}
void add_log_line(char* the_line)
{
log_last_ptr++;
if (log_last_ptr >= MAX_LOG_MESSAGES)
{
log_last_ptr = 0;
}
log_lines_stored++;
if (log_lines_stored > MAX_LOG_MESSAGES - 1)
{
log_lines_stored = MAX_LOG_MESSAGES - 1;
}
/* copy the message to our buffer
*/
strncpy(log_messages[log_last_ptr], the_line, LOG_WINDOW_WIDTH - 2);
}
void add_log_message(char* msg)
{
int msg_len = strlen(msg);
char* ptr;
if (msg_len > LOG_WINDOW_WIDTH - 2)
{
ptr = msg;
while (ptr - msg < msg_len)
{ add_log_line(ptr);
ptr += LOG_WINDOW_WIDTH - 2;
}
}
else
{
add_log_line(msg);
}
}
int main(int argc, char* argv[])
{
char buf[100];
int i = 0;
int this_char;
initscr(); /* Start curses mode */
/* Disable all system handling of any keys EXCEPT
* CTRL-C and CTRL-z
*/
cbreak();
/* enable curses color capability
*/
start_color();
keypad(stdscr, TRUE); /* We get F1, F2 etc.. */
/* Don't echo() characters to the screen while we do getch().
* you can turn the chars back on with 'echo()'
*/
noecho();
init_pair(RED_ON_BLACK, COLOR_RED, COLOR_BLACK);
init_pair(GREEN_ON_BLACK, COLOR_GREEN, COLOR_BLACK);
init_pair(CYAN_ON_BLACK, COLOR_CYAN, COLOR_BLACK);
init_pair(WINDOW_FILL_COLOR, COLOR_BLACK, COLOR_WHITE);
/* Create the log window
*/
log_window = newwin(LOG_WINDOW_HEIGHT,
LOG_WINDOW_WIDTH,
LOG_WINDOW_Y_POS,
LOG_WINDOW_X_POS);
wrefresh(log_window);
while(1)
{
this_char = getch();
switch(this_char)
{
case KEY_DOWN:
scroll_log_window(-1);
break;
case KEY_UP:
scroll_log_window(1);
break;
case KEY_HOME:
scroll_log_home();
break;
case KEY_LL:
scroll_log_end();
break;
case KEY_NPAGE:
scroll_log_page_down();
break;
case KEY_PPAGE:
scroll_log_page_up();
break;
default:
sprintf(buf, "line %d here this is some really neat stuff, no??? sure really (%c)", i++, this_char);
add_log_message(buf);
break;
}
render_log_window();
wrefresh(log_window);
}
endwin();
}
The Makefile
CC = gcc
SRCS = # panel.c
OBJS = $(SRCS:.c=.o)
LIBPATH = -L.
LIBS = -lncurses -lpanel
INCLS = -I.
CFLAGS = $(INCLS) $(LIBS) -Wall -g
TARGET = production_tester
default: all
all: $(TARGET) panel
$(OBJS): Makefile
$(TARGET): $(OBJS) $(TARGET).c
$(CC) -o $(TARGET) $(TARGET).c $(OBJS) $(CFLAGS)
panel: panel.c
$(CC) -o panel panel.c $(CFLAGS)
clean:
$(RM) *.o $(TARGET) core
--
MattWalsh - 12 Apr 2002