Commit 2bb04f01 authored by Cedric Roux's avatar Cedric Roux

first version of the generic gui for the tracer

parent ee6789cd
......@@ -10,11 +10,14 @@ CFLAGS += -DT_USE_SHARED_MEMORY
LIBS += -lrt
PROG=tracer
OBJS=main.o plot.o database.o forward.o
OBJS=main.o plot.o database.o forward.o gui/gui.a
$(PROG): $(OBJS)
$(CC) $(CFLAGS) -o $(PROG) $(OBJS) $(LIBS)
gui/gui.a:
cd gui && make
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
......@@ -22,3 +25,4 @@ main.o: ../T_IDs.h ../T_defs.h
clean:
rm -f *.o $(PROG) core
cd gui && make clean
CC=gcc
CFLAGS=-Wall -g -pthread
OBJS=init.o loop.o toplevel_window.o x.o container.o widget.o \
gui.o label.o event.o xy_plot.o text_list.o
gui.a: $(OBJS)
ar cr gui.a $(OBJS)
test: test.o gui.a
$(CC) -o test $(OBJS) test.o -lX11 -pthread -lm
%.o: %.c
$(CC) $(CFLAGS) -o $@ -c $<
clean:
rm -f *.a *.o test
#include "gui.h"
#include "gui_defs.h"
#include <stdio.h>
#include <stdlib.h>
static void repack(gui *g, widget *_this)
{
printf("REPACK container %p\n", _this);
struct container_widget *this = _this;
this->hints_are_valid = 0;
return this->common.parent->repack(g, this->common.parent);
}
static void add_child(gui *g, widget *this, widget *child, int position)
{
printf("ADD_CHILD container\n");
widget_add_child_internal(g, this, child, position);
}
static void compute_vertical_hints(struct gui *g,
struct container_widget *this)
{
struct widget_list *l;
int cwidth, cheight;
int allocated_width = 0, allocated_height = 0;
/* get largest width */
l = this->common.children;
while (l) {
l->item->hints(g, l->item, &cwidth, &cheight);
if (cwidth > allocated_width) allocated_width = cwidth;
allocated_height += cheight;
l = l->next;
}
this->hint_width = allocated_width;
this->hint_height = allocated_height;
this->hints_are_valid = 1;
}
static void compute_horizontal_hints(struct gui *g,
struct container_widget *this)
{
struct widget_list *l;
int cwidth, cheight;
int allocated_width = 0, allocated_height = 0;
/* get largest height */
l = this->common.children;
while (l) {
l->item->hints(g, l->item, &cwidth, &cheight);
if (cheight > allocated_height) allocated_height = cheight;
allocated_width += cwidth;
l = l->next;
}
this->hint_width = allocated_width;
this->hint_height = allocated_height;
this->hints_are_valid = 1;
}
static void vertical_allocate(gui *_gui, widget *_this,
int x, int y, int width, int height)
{
printf("ALLOCATE container vertical %p\n", _this);
int cy = 0;
int cwidth, cheight;
struct gui *g = _gui;
struct container_widget *this = _this;
struct widget_list *l;
if (this->hints_are_valid == 1) goto hints_ok;
compute_vertical_hints(g, this);
hints_ok:
this->common.x = x;
this->common.y = y;
this->common.width = width;
this->common.height = height;
/* allocate */
l = this->common.children;
while (l) {
l->item->hints(g, l->item, &cwidth, &cheight);
l->item->allocate(g, l->item, this->common.x, this->common.y + cy,
//this->hint_width, cheight);
width, cheight);
cy += cheight;
l = l->next;
}
if (cy != this->hint_height) ERR("reachable?\n");
}
static void horizontal_allocate(gui *_gui, widget *_this,
int x, int y, int width, int height)
{
printf("ALLOCATE container horizontal %p\n", _this);
int cx = 0;
int cwidth, cheight;
struct gui *g = _gui;
struct container_widget *this = _this;
struct widget_list *l;
if (this->hints_are_valid == 1) goto hints_ok;
compute_horizontal_hints(g, this);
hints_ok:
this->common.x = x;
this->common.y = y;
this->common.width = width;
this->common.height = height;
/* allocate */
l = this->common.children;
while (l) {
l->item->hints(g, l->item, &cwidth, &cheight);
l->item->allocate(g, l->item, this->common.x + cx, this->common.y,
cwidth, this->hint_height);
cx += cwidth;
l = l->next;
}
if (cx != this->hint_width) ERR("reachable?\n");
}
static void vertical_hints(gui *_gui, widget *_w, int *width, int *height)
{
printf("HINTS container vertical %p\n", _w);
struct gui *g = _gui;
struct container_widget *this = _w;
if (this->hints_are_valid) {
*width = this->hint_width;
*height = this->hint_height;
return;
}
compute_vertical_hints(g, this);
*width = this->hint_width;
*height = this->hint_height;
}
static void horizontal_hints(gui *_gui, widget *_w, int *width, int *height)
{
printf("HINTS container horizontal %p\n", _w);
struct gui *g = _gui;
struct container_widget *this = _w;
if (this->hints_are_valid) {
*width = this->hint_width;
*height = this->hint_height;
return;
}
compute_horizontal_hints(g, this);
*width = this->hint_width;
*height = this->hint_height;
}
static void paint(gui *_gui, widget *_this)
{
printf("PAINT container\n");
struct gui *g = _gui;
struct widget *this = _this;
struct widget_list *l;
l = this->children;
while (l) {
l->item->paint(g, l->item);
l = l->next;
}
}
widget *new_container(gui *_gui, int vertical)
{
struct gui *g = _gui;
struct container_widget *w;
glock(g);
w = new_widget(g, CONTAINER, sizeof(struct container_widget));
w->vertical = vertical;
w->hints_are_valid = 0;
w->common.paint = paint;
w->common.add_child = add_child;
w->common.repack = repack;
if (vertical) {
w->common.allocate = vertical_allocate;
w->common.hints = vertical_hints;
} else {
w->common.allocate = horizontal_allocate;
w->common.hints = horizontal_hints;
}
gunlock(g);
return w;
}
#include "gui.h"
#include "gui_defs.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdarg.h>
/*****************************************************************/
/* generic functions */
/*****************************************************************/
static void event_list_append(struct gui *g, struct event *e)
{
struct event_list *new;
new = calloc(1, sizeof(struct event_list));
if (new == NULL) OOM;
new->item = e;
if (g->queued_events == NULL) {
g->queued_events = new;
new->last = new;
return;
}
g->queued_events->last->next = new;
g->queued_events->last = new;
}
static void free_event(struct event *e)
{
switch (e->type) {
case REPACK: /* nothing */ break;
case DIRTY: /* nothing */ break;
}
free(e);
}
/*****************************************************************/
/* sending events */
/*****************************************************************/
static event *new_event_repack(int id)
{
struct repack_event *ret;
ret = calloc(1, sizeof(struct repack_event));
if (ret == NULL) OOM;
ret->id = id;
return ret;
}
static event *new_event_dirty(int id)
{
struct dirty_event *ret;
ret = calloc(1, sizeof(struct dirty_event));
if (ret == NULL) OOM;
ret->id = id;
return ret;
}
void send_event(gui *_gui, enum event_type type, ...)
{
printf("send_event %d\n", type);
struct gui *g = _gui;
int do_write = 0;
va_list ap;
struct event *e;
if (g->queued_events == NULL) do_write = 1;
va_start(ap, type);
switch (type) {
case REPACK: {
int id;
id = va_arg(ap, int);
e = new_event_repack(id);
break;
}
case DIRTY: {
int id;
id = va_arg(ap, int);
e = new_event_dirty(id);
break;
}
}
va_end(ap);
e->type = type;
event_list_append(g, e);
if (do_write) {
char c = 1;
if (write(g->event_pipe[1], &c, 1) != 1)
ERR("error writing to pipe: %s\n", strerror(errno));
}
}
/*****************************************************************/
/* processing events */
/*****************************************************************/
static void repack_event(struct gui *g, int id)
{
struct widget *w = find_widget(g, id);
if (w == NULL) { WARN("widget id %d not found\n", id); return; }
w->repack(g, w);
}
/* TODO: put that function somewhere else? */
static struct toplevel_window_widget *get_toplevel_window(struct widget *w)
{
while (w != NULL) {
if (w->type == TOPLEVEL_WINDOW)
return (struct toplevel_window_widget *)w;
w = w->parent;
}
return NULL;
}
static void dirty_event(struct gui *g, int id)
{
struct widget *w = find_widget(g, id);
struct toplevel_window_widget *win;
if (w == NULL) { WARN("widget id %d not found\n", id); return; }
win = get_toplevel_window(w);
if (win == NULL)
{ WARN("widget id %d not contained in a window\n", id); return; }
g->xwin = win->x;
w->paint(g, w);
g->xwin = NULL;
g->repainted = 1;
}
static void process_event(struct gui *g, struct event *e)
{
printf("processing event type %d\n", e->type);
switch (e->type) {
case REPACK: repack_event(g, ((struct repack_event *)e)->id); break;
case DIRTY: dirty_event(g, ((struct dirty_event *)e)->id); break;
}
}
/* TODO: events' compression */
void gui_events(gui *_gui)
{
struct gui *g = _gui;
printf("gui_events START: head %p\n", g->queued_events);
while (g->queued_events) {
struct event_list *cur = g->queued_events;
g->queued_events = cur->next;
if (g->queued_events) g->queued_events->last = cur->last;
process_event(g, cur->item);
free_event(cur->item);
free(cur);
}
printf("gui_events DONE\n");
}
#include "gui.h"
#include "gui_defs.h"
#include "x.h"
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void glock(gui *_gui)
{
struct gui *g = _gui;
if (pthread_mutex_lock(g->lock)) ERR("mutex error\n");
}
void gunlock(gui *_gui)
{
struct gui *g = _gui;
if (pthread_mutex_unlock(g->lock)) ERR("mutex error\n");
}
int new_color(gui *_gui, char *color)
{
struct gui *g = _gui;
return x_new_color(g->x, color);
}
#ifndef _GUI_H_
#define _GUI_H_
/* defines the public API of the GUI */
typedef void gui;
typedef void widget;
#define HORIZONTAL 0
#define VERTICAL 1
gui *gui_init(void);
/* position = -1 to put at the end */
void widget_add_child(gui *gui, widget *parent, widget *child, int position);
widget *new_toplevel_window(gui *gui, int width, int height, char *title);
widget *new_container(gui *gui, int vertical);
widget *new_label(gui *gui, const char *text);
widget *new_xy_plot(gui *gui, int width, int height, char *label,
int vruler_width);
widget *new_text_list(gui *_gui, int width, int nlines, int background_color);
void xy_plot_set_range(gui *gui, widget *this,
float xmin, float xmax, float ymin, float ymax);
void text_list_add(gui *gui, widget *this, const char *text, int position);
void gui_loop(gui *gui);
void glock(gui *gui);
void gunlock(gui *gui);
int new_color(gui *gui, char *color);
#endif /* _GUI_H_ */
#ifndef _GUI_DEFS_H_
#define _GUI_DEFS_H_
/* defines the private API of the GUI */
/*************************************************************************/
/* logging macros */
/*************************************************************************/
#define ERR(...) \
do { \
printf("%s:%d:%s: ERROR: ", __FILE__, __LINE__, __FUNCTION__); \
printf(__VA_ARGS__); \
abort(); \
} while (0)
#define WARN(...) \
do { \
printf("%s:%d:%s: WARNING: ", __FILE__, __LINE__, __FUNCTION__); \
printf(__VA_ARGS__); \
} while (0)
#define OOM ERR("out of memory\n")
/*************************************************************************/
/* widgets */
/*************************************************************************/
enum widget_type {
TOPLEVEL_WINDOW, CONTAINER, TEXT_LIST, XY_PLOT, BUTTON, LABEL
};
struct widget_list;
struct widget {
enum widget_type type;
int id;
int x; /* allocated x after packing */
int y; /* allocated y after packing */
int width; /* allocated width after packing */
int height; /* allocated height after packing */
struct widget_list *children;
struct widget *parent;
void (*repack)(gui *g, widget *this);
void (*add_child)(gui *g, widget *this, widget *child, int position);
void (*allocate)(gui *g, widget *this, int x, int y, int width, int height);
void (*hints)(gui *g, widget *this, int *width, int *height);
void (*paint)(gui *g, widget *this);
};
struct widget_list {
struct widget *item;
struct widget_list *next;
//struct widget_list *prev; /* unused? */
struct widget_list *last; /* valid only for the head of the list */
};
struct toplevel_window_widget {
struct widget common;
void *x; /* opaque X data (type x_window), used in x.c */
};
struct container_widget {
struct widget common;
int vertical;
int hints_are_valid; /* used to cache hints values */
int hint_width; /* cached hint values - invalid if */
int hint_height; /* repack_was_called == 1 */
};
struct text_list_widget {
struct widget common;
char **text;
int text_count;
int wanted_width;
int wanted_nlines; /* number of lines of text the user wants to see */
int allocated_nlines; /* actual number of visible lines */
int starting_line; /* points to the first visible line of text */
int line_height;
int baseline;
int background_color;
};
struct xy_plot_widget {
struct widget common;
float *x;
float *y;
int npoints;
char *label;
int label_width;
int label_height;
int label_baseline;
int vrule_width; /* the width of the vertical ruler text zone */
float xmin, xmax;
float ymin, ymax;
int wanted_width;
int wanted_height;
};
struct button_widget {
struct widget common;
};
struct label_widget {
struct widget common;
const char *t;
int color;
int width; /* as given by the graphic's backend */
int height; /* as given by the graphic's backend */
int baseline; /* as given by the graphic's backend */
};
/*************************************************************************/
/* events */
/*************************************************************************/
typedef void event;
enum event_type {
DIRTY, REPACK
};
struct event {
enum event_type type;
};
struct event_list {
struct event *item;
struct event_list *next;
struct event_list *last;
};
struct dirty_event {
struct event common;
int id;
};
struct repack_event {
struct event common;
int id;
};
/*************************************************************************/
/* main structure */
/*************************************************************************/
struct gui {
void *lock;
void *x; /* opaque X data (type x_connection), used in x.c */
struct widget_list *toplevel;
struct event_list *queued_events;
int event_pipe[2];
int next_id; /* tells what is the ID of
the next created widget */
int repainted; /* set to 1 when some widget has
* been repainted (TODO: can be any,
* to be optimized) */
void *xwin; /* set by a toplevel_window when
* it paints itself, to be used
* by its children */
};
/*************************************************************************/
/* internal functions */
/*************************************************************************/
widget *new_widget(struct gui *g, enum widget_type type, int size);
void widget_add_child_internal(
gui *_gui, widget *parent, widget *child, int position);
const char *widget_name(enum widget_type type);
void send_event(gui *gui, enum event_type type, ...);
void gui_events(gui *gui);
struct widget *find_widget(struct gui *g, int id);
#endif /* _GUI_DEFS_H_ */
#include "gui.h"
#include "gui_defs.h"
#include "x.h"
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
gui *gui_init(void)
{
struct gui *ret;
ret = calloc(1, sizeof(struct gui));
if (ret == NULL) OOM;
ret->lock = malloc(sizeof(pthread_mutex_t));
if (ret->lock == NULL) OOM;
if (pthread_mutex_init(ret->lock, NULL))
ERR("mutex initialization failed\n");
if (pipe(ret->event_pipe))
ERR("%s\n", strerror(errno));
ret->x = x_open();
return ret;
}
#include "gui.h"
#include "gui_defs.h"
#include "x.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void paint(gui *_gui, widget *_w)
{
struct gui *g = _gui;
struct label_widget *l = _w;
printf("PAINT label '%s'\n", l->t);
x_draw_string(g->x, g->xwin, l->color,
l->common.x, l->common.y + l->baseline, l->t);
}
static void hints(gui *_gui, widget *_w, int *width, int *height)
{
struct label_widget *l = _w;
printf("HINTS label '%s'\n", l->t);
*width = l->width;
*height = l->height;
}
widget *new_label(gui *_gui, const char *label)
{
struct gui *g = _gui;
struct label_widget *w;
glock(g);
w = new_widget(g, LABEL, sizeof(struct label_widget));
w->t = strdup(label);
if (w->t == NULL) OOM;
w->color = FOREGROUND_COLOR;
x_text_get_dimensions(g->x, label, &w->width, &w->height, &w->baseline);
w->common.paint = paint;
w->common.hints = hints;
gunlock(g);
return w;
}
#include "gui.h"
#include "gui_defs.h"
#include "x.h"
#include <sys/select.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
void gui_loop(gui *_gui)
{
struct gui *g = _gui;
int xfd;
int eventfd;
int maxfd;
fd_set rd;
xfd = x_connection_fd(g->x);
eventfd = g->event_pipe[0];
if (eventfd > xfd) maxfd = eventfd;
else maxfd = xfd;
while (1) {
x_flush(g->x);
FD_ZERO(&rd);
FD_SET(xfd, &rd);
FD_SET(eventfd, &rd);
if (select(maxfd+1, &rd, NULL, NULL, NULL) == -1)
ERR("select: %s\n", strerror(errno));
glock(g);
if (FD_ISSET(xfd, &rd))
x_events(g);
if (FD_ISSET(eventfd, &rd)) {
char c[256];
if (read(eventfd, c, 256)); /* for no gcc warnings */
}
gui_events(g);
if (g->repainted) {
struct widget_list *cur;
g->repainted = 0;
cur = g->toplevel;
while (cur) {
struct toplevel_window_widget *w =
(struct toplevel_window_widget *)cur->item;
x_draw(g->x, w->x);
cur = cur->next;
}
}
gunlock(g);
}
}
#include "gui.h"
int main(void)
{
gui *g;
widget *w, *c1, *c2, *l, *plot, *tl;
int tlcol;
g = gui_init();
c1 = new_container(g, VERTICAL);
c2 = new_container(g, HORIZONTAL);
l = new_label(g, "this is a good label");
widget_add_child(g, c2, l, 0);
l = new_label(g, "this is another good label");
widget_add_child(g, c2, l, -1);
l = new_label(g, "OH! WHAT A LABEL!");
widget_add_child(g, c1, l, -1);
widget_add_child(g, c1, c2, 0);
plot = new_xy_plot(g, 100, 100, "xy plot test", 30);
#if 0
c2 = new_container(g, HORIZONTAL);
widget_add_child(g, c2, plot, -1);
widget_add_child(g, c1, c2, -1);
#else
widget_add_child(g, c1, plot, -1);
#endif
tlcol = new_color(g, "#ddf");
tl = new_text_list(g, 300, 10, tlcol);
widget_add_child(g, c1, tl, -1);
text_list_add(g, tl, "hello", -1);
text_list_add(g, tl, "world", -1);
w = new_toplevel_window(g, 500, 400, "test window");
widget_add_child(g, w, c1, 0);
gui_loop(g);
return 0;
}
#include "gui.h"
#include "gui_defs.h"
#include "x.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void paint(gui *_gui, widget *_this)
{
struct gui *g = _gui;
struct text_list_widget *this = _this;
int i, j;
printf("PAINT text_list %p xywh %d %d %d %d\n", _this, this->common.x, this->common.y, this->common.width, this->common.height);
x_fill_rectangle(g->x, g->xwin, this->background_color,
this->common.x, this->common.y,
this->common.width, this->common.height);
for (i = 0, j = this->starting_line;
i < this->allocated_nlines && j < this->text_count; i++, j++)
x_draw_string(g->x, g->xwin, FOREGROUND_COLOR,
this->common.x,
this->common.y + i * this->line_height + this->baseline,
this->text[j]);
}
static void hints(gui *_gui, widget *_w, int *width, int *height)
{
struct text_list_widget *w = _w;
*width = w->wanted_width;
*height = w->wanted_nlines * w->line_height;
printf("HINTS text_list wh %d %d\n", *width, *height);
}
static void allocate(
gui *gui, widget *_this, int x, int y, int width, int height)
{
struct text_list_widget *this = _this;
this->common.x = x;
this->common.y = y;
this->common.width = width;
this->common.height = height;
this->allocated_nlines = height / this->line_height;
printf("ALLOCATE text_list %p xywh %d %d %d %d nlines %d\n", this, x, y, width, height, this->allocated_nlines);
}
widget *new_text_list(gui *_gui, int width, int nlines, int bgcol)
{
struct gui *g = _gui;
struct text_list_widget *w;
int dummy;
glock(g);
w = new_widget(g, TEXT_LIST, sizeof(struct text_list_widget));
w->wanted_nlines = nlines;
x_text_get_dimensions(g->x, ".", &dummy, &w->line_height, &w->baseline);
w->background_color = bgcol;
w->wanted_width = width;
w->common.paint = paint;
w->common.hints = hints;
w->common.allocate = allocate;
gunlock(g);
return w;
}
/*************************************************************************/
/* public functions */
/*************************************************************************/
void text_list_add(gui *_gui, widget *_this, const char *text, int position)
{
struct gui *g = _gui;
struct text_list_widget *this = _this;
glock(g);
if (position < 0) position = this->text_count;
if (position > this->text_count) position = this->text_count;
this->text_count++;
this->text = realloc(this->text, this->text_count * sizeof(char *));
if (this->text == NULL) OOM;
memmove(this->text + position + 1, this->text + position,
(this->text_count-1 - position) * sizeof(char *));
this->text[position] = strdup(text); if (this->text[position] == NULL) OOM;
send_event(g, DIRTY, this->common.id);
gunlock(g);
}
#include "gui.h"
#include "gui_defs.h"
#include "x.h"
#include <stdio.h>
#include <stdlib.h>
/**********************************************************************/
/* callback functions */
/**********************************************************************/
static void repack(gui *g, widget *_this)
{
printf("REPACK toplevel_window\n");
struct toplevel_window_widget *this = _this;
if (this->common.children == NULL) ERR("toplevel window has no child\n");
if (this->common.children->next != NULL)
ERR("toplevel window has too much children\n");
this->common.children->item->allocate(g, this->common.children->item,
0 /* x */, 0 /* y */, this->common.width, this->common.height);
send_event(g, DIRTY, this->common.id);
}
static void add_child(gui *_gui, widget *_this, widget *child, int position)
{
printf("ADD_CHILD toplevel_window\n");
struct widget *this = _this;
if (this->children != NULL) {
WARN("toplevel window already has a child\n");
return;
}
if (position)
WARN("toplevel window doesn't care about 'position' "
"(you passed %d, you should always pass 0)\n",
position);
widget_add_child_internal(_gui, _this, child, 0); /* this does the REPACK */
}
/* called when the underlying window is resized by the user or the system */
static void allocate(
gui *_gui, widget *_this, int x, int y, int width, int height)
{
printf("ALLOCATE toplevel_window\n");
struct toplevel_window_widget *this = _this;
this->common.width = width;
this->common.height = height;
// repack(_gui, _this);
send_event(_gui, REPACK, this->common.id);
}
static void paint(gui *_gui, widget *_this)
{
struct gui *g = _gui;
struct toplevel_window_widget *this = _this;
printf("PAINT toplevel_window (%d %d)\n", this->common.width, this->common.height);
x_fill_rectangle(g->x, this->x, BACKGROUND_COLOR,
0, 0, this->common.width, this->common.height);
g->xwin = this->x;
this->common.children->item->paint(_gui, this->common.children->item);
g->xwin = NULL; /* TODO: remove? it's just in case */
}
/**********************************************************************/
/* creation */
/**********************************************************************/
widget *new_toplevel_window(gui *_gui, int width, int height, char *title)
{
struct gui *g = _gui;
struct toplevel_window_widget *w;
glock(g);
w = new_widget(g, TOPLEVEL_WINDOW, sizeof(struct toplevel_window_widget));
w->common.width = width;
w->common.height = height;
w->x = x_create_window(g->x, width, height, title);
w->common.repack = repack;
w->common.add_child = add_child;
w->common.allocate = allocate;
w->common.paint = paint;
gunlock(g);
return w;
}
#include "gui.h"
#include "gui_defs.h"
#include "x.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
static void default_repack(gui *gui, widget *_this);
static void default_allocate(
gui *gui, widget *_this, int x, int y, int width, int height);
static void default_add_child(
gui *_gui, widget *_this, widget *child, int position);
static void default_hints(gui *g, widget *this, int *width, int *height);
static void toplevel_list_append(struct gui *g, struct widget *e)
{
struct widget_list *new;
new = calloc(1, sizeof(struct widget_list));
if (new == NULL) OOM;
new->item = e;
if (g->toplevel == NULL) {
g->toplevel = new;
new->last = new;
return;
}
g->toplevel->last->next = new;
g->toplevel->last = new;
}
widget *new_widget(struct gui *g, enum widget_type type, int size)
{
struct widget *ret;
//glock(g);
ret = calloc(1, size);
if (ret == NULL) OOM;
ret->repack = default_repack;
ret->add_child = default_add_child;
ret->allocate = default_allocate;
ret->hints = default_hints;
/* there is no default paint, on purpose */
ret->type = type;
ret->id = g->next_id;
g->next_id++;
ret->width = 0;
ret->height = 0;
/* add toplevel windows to g->toplevel */
if (type == TOPLEVEL_WINDOW)
toplevel_list_append(g, ret);
//gunlock(g);
return ret;
}
/*************************************************************************/
/* internal functions */
/*************************************************************************/
void widget_add_child_internal(
gui *_gui, widget *parent, widget *child, int position)
{
struct widget *p = parent;
struct widget *c = child;
struct widget_list *new;
struct widget_list *prev, *cur;
int i;
new = calloc(1, sizeof(struct widget_list));
if (new == NULL) OOM;
new->item = c;
c->parent = p;
prev = NULL;
cur = p->children;
for (i = 0; position < 0 || i < position; i++) {
if (cur == NULL) break;
prev = cur;
cur = cur->next;
}
/* TODO: warn/err if i != position+1? */
if (prev == NULL) {
/* new is at head */
new->next = p->children;
if (p->children != NULL) new->last = p->children->last;
else new->last = new;
p->children = new;
goto repack;
}
if (cur == NULL) {
/* new is at tail */
prev->next = new;
p->children->last = new;
goto repack;
}
/* new is between two existing items */
prev->next = new;
new->next = cur;
repack:
send_event(_gui, REPACK, p->id);
}
/*************************************************************************/
/* default functions */
/*************************************************************************/
static void default_repack(gui *gui, widget *_this)
{
struct widget *this = _this;
return this->parent->repack(gui, this->parent);
}
static void default_add_child(
gui *_gui, widget *_this, widget *child, int position)
{
struct widget *this = _this;
WARN("cannot add child to widget %s\n", widget_name(this->type));
}
static void default_allocate(
gui *gui, widget *_this, int x, int y, int width, int height)
{
struct widget *this = _this;
this->x = x;
this->y = y;
this->width = width;
this->height = height;
}
static void default_hints(gui *g, widget *this, int *width, int *height)
{
*width = 1;
*height = 1;
}
/*************************************************************************/
/* utils functions */
/*************************************************************************/
void widget_add_child(gui *_gui, widget *parent, widget *child, int position)
{
struct widget *this = parent;
glock(_gui);
this->add_child(_gui, parent, child, position);
gunlock(_gui);
}
static const char *names[] = {
"TOPLEVEL_WINDOW", "CONTAINER", "TEXT_LIST", "XY_PLOT", "BUTTON", "LABEL"
};
const char *widget_name(enum widget_type type)
{
switch (type) {
default: break;
case TOPLEVEL_WINDOW:
case CONTAINER:
case TEXT_LIST:
case XY_PLOT:
case BUTTON:
case LABEL:
return names[type];
}
return "UNKNOWN (error)";
}
/*************************************************************************/
/* find a widget */
/*************************************************************************/
/* TODO: optimize traversal and also use a cache */
struct widget *_find_widget(struct widget *c, int id)
{
struct widget_list *l;
struct widget *ret;
if (c == NULL) return NULL;
if (c->id == id) return c;
l = c->children;
while (l) {
ret = _find_widget(l->item, id);
if (ret != NULL) return ret;
l = l->next;
}
return NULL;
}
struct widget *find_widget(struct gui *g, int id)
{
struct widget_list *l;
struct widget *ret;
l = g->toplevel;
while (l) {
ret = _find_widget(l->item, id);
if (ret != NULL) return ret;
l = l->next;
}
return NULL;
}
#include "x.h"
#include "x_defs.h"
#include "gui_defs.h"
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int x_connection_fd(x_connection *_x)
{
struct x_connection *x = _x;
return ConnectionNumber(x->d);
}
static GC create_gc(Display *d, char *color)
{
GC ret = XCreateGC(d, DefaultRootWindow(d), 0, NULL);
XGCValues gcv;
XColor rcol, scol;
XCopyGC(d, DefaultGC(d, DefaultScreen(d)), -1L, ret);
if (XAllocNamedColor(d, DefaultColormap(d, DefaultScreen(d)),
color, &scol, &rcol)) {
gcv.foreground = scol.pixel;
XChangeGC(d, ret, GCForeground, &gcv);
} else ERR("X: could not allocate color '%s'\n", color);
return ret;
}
int x_new_color(x_connection *_x, char *color)
{
struct x_connection *x = _x;
x->ncolors++;
x->colors = realloc(x->colors, x->ncolors * sizeof(GC));
if (x->colors == NULL) OOM;
x->colors[x->ncolors-1] = create_gc(x->d, color);
return x->ncolors - 1;
}
x_connection *x_open(void)
{
struct x_connection *ret;
ret = calloc(1, sizeof(struct x_connection));
if (ret == NULL) OOM;
ret->d = XOpenDisplay(0);
printf("XOpenDisplay display %p return x_connection %p\n", ret->d, ret);
if (ret->d == NULL) ERR("error calling XOpenDisplay: no X? you root?\n");
x_new_color(ret, "white"); /* background color */
x_new_color(ret, "black"); /* foreground color */
return ret;
}
x_window *x_create_window(x_connection *_x, int width, int height,
char *title)
{
struct x_connection *x = _x;
struct x_window *ret;
ret = calloc(1, sizeof(struct x_window));
if (ret == NULL) OOM;
ret->w = XCreateSimpleWindow(x->d, DefaultRootWindow(x->d), 0, 0,
width, height, 0, WhitePixel(x->d, DefaultScreen(x->d)),
WhitePixel(x->d, DefaultScreen(x->d)));
ret->width = width;
ret->height = height;
XStoreName(x->d, ret->w, title);
ret->p = XCreatePixmap(x->d, ret->w, width, height,
DefaultDepth(x->d, DefaultScreen(x->d)));
/* enable backing store */
{
XSetWindowAttributes att;
att.backing_store = Always;
XChangeWindowAttributes(x->d, ret->w, CWBackingStore, &att);
}
XSelectInput(x->d, ret->w,
KeyPressMask |
ButtonPressMask |
ButtonReleaseMask |
PointerMotionMask |
ExposureMask |
StructureNotifyMask);
XMapWindow(x->d, ret->w);
#if 0
/* wait for window to be mapped */
printf("wait for map\n");
while (1) {
XEvent ev;
//XWindowEvent(x->d, ret->w, StructureNotifyMask, &ev);
XWindowEvent(x->d, ret->w, ExposureMask, &ev);
printf("got ev %d\n", ev.type);
//if (ev.type == MapNotify) break;
if (ev.type == Expose) break;
}
printf("XXX create connection %p window %p (win id %d pixmap %d) w h %d %d\n", x, ret, (int)ret->w, (int)ret->p, width, height);
#endif
return ret;
}
static struct toplevel_window_widget *find_x_window(struct gui *g, Window id)
{
struct widget_list *cur;
struct toplevel_window_widget *w;
struct x_window *xw;
cur = g->toplevel;
while (cur) {
w = (struct toplevel_window_widget *)cur->item;
xw = w->x;
if (xw->w == id) return w;
cur = cur->next;
}
return NULL;
}
void x_events(gui *_gui)
{
struct gui *g = _gui;
struct widget_list *cur;
struct x_connection *x = g->x;
struct toplevel_window_widget *w;
printf("x_events START\n");
/* preprocessing (to "compress" events) */
cur = g->toplevel;
while (cur) {
struct x_window *xw;
w = (struct toplevel_window_widget *)cur->item;
xw = w->x;
xw->redraw = 0;
xw->repaint = 0;
xw->resize = 0;
cur = cur->next;
}
while (XPending(x->d)) {
XEvent ev;
XNextEvent(x->d, &ev);
printf("XEV %d\n", ev.type);
switch (ev.type) {
case Expose:
if ((w = find_x_window(g, ev.xexpose.window)) != NULL) {
struct x_window *xw = w->x;
xw->redraw = 1;
}
break;
case ConfigureNotify:
if ((w = find_x_window(g, ev.xexpose.window)) != NULL) {
struct x_window *xw = w->x;
xw->resize = 1;
xw->new_width = ev.xconfigure.width;
xw->new_height = ev.xconfigure.height;
if (xw->new_width < 10) xw->new_width = 10;
if (xw->new_height < 10) xw->new_height = 10;
printf("ConfigureNotify %d %d\n", ev.xconfigure.width, ev.xconfigure.height);
}
break;
#if 0
case MapNotify:
if ((w = find_x_window(g, ev.xexpose.window)) != NULL) {
struct x_window *xw = w->x;
xw->repaint = 1;
}
break;
#endif
default: WARN("TODO: X event type %d\n", ev.type); break;
}
}
/* postprocessing */
printf("post processing\n");
cur = g->toplevel;
while (cur) {
struct toplevel_window_widget *w =
(struct toplevel_window_widget *)cur->item;
struct x_window *xw = w->x;
if (xw->resize) {
printf("resize old %d %d new %d %d\n", xw->width, xw->height, xw->new_width, xw->new_height);
if (xw->width != xw->new_width || xw->height != xw->new_height) {
w->common.allocate(g, w, 0, 0, xw->new_width, xw->new_height);
xw->width = xw->new_width;
xw->height = xw->new_height;
XFreePixmap(x->d, xw->p);
xw->p = XCreatePixmap(x->d, xw->w, xw->width, xw->height,
DefaultDepth(x->d, DefaultScreen(x->d)));
//xw->repaint = 1;
}
}
if (xw->repaint) {
w->common.paint(g, w);
xw->redraw = 1;
}
if (xw->redraw) {
struct x_connection *x = g->x;
printf("XCopyArea w h %d %d\n", xw->width, xw->height);
XCopyArea(x->d, xw->p, xw->w, x->colors[1],
0, 0, xw->width, xw->height, 0, 0);
}
cur = cur->next;
}
printf("x_events DONE\n");
}
void x_flush(x_connection *_x)
{
struct x_connection *x = _x;
XFlush(x->d);
}
void x_text_get_dimensions(x_connection *_c, const char *t,
int *width, int *height, int *baseline)
{
struct x_connection *c = _c;
int dir;
int ascent;
int descent;
XCharStruct overall;
/* TODO: don't use XQueryTextExtents (X roundtrip) */
XQueryTextExtents(c->d, XGContextFromGC(c->colors[1]), t, strlen(t),
&dir, &ascent, &descent, &overall);
//printf("dir %d ascent %d descent %d lbearing %d rbearing %d width %d ascent %d descent %d\n", dir, ascent, descent, overall.lbearing, overall.rbearing, overall.width, overall.ascent, overall.descent);
*width = overall.width;
*height = ascent + descent;
*baseline = ascent;
}
/***********************************************************************/
/* public drawing functions */
/***********************************************************************/
void x_draw_line(x_connection *_c, x_window *_w, int color,
int x1, int y1, int x2, int y2)
{
struct x_connection *c = _c;
struct x_window *w = _w;
XDrawLine(c->d, w->p, c->colors[color], x1, y1, x2, y2);
}
void x_draw_rectangle(x_connection *_c, x_window *_w, int color,
int x, int y, int width, int height)
{
struct x_connection *c = _c;
struct x_window *w = _w;
XDrawRectangle(c->d, w->p, c->colors[color], x, y, width, height);
}
void x_fill_rectangle(x_connection *_c, x_window *_w, int color,
int x, int y, int width, int height)
{
struct x_connection *c = _c;
struct x_window *w = _w;
XFillRectangle(c->d, w->p, c->colors[color], x, y, width, height);
}
void x_draw_string(x_connection *_c, x_window *_w, int color,
int x, int y, const char *t)
{
struct x_connection *c = _c;
struct x_window *w = _w;
int tlen = strlen(t);
XDrawString(c->d, w->p, c->colors[color], x, y, t, tlen);
}
void x_draw(x_connection *_c, x_window *_w)
{
struct x_connection *c = _c;
struct x_window *w = _w;
printf("x_draw XCopyArea w h %d %d display %p window %d pixmap %d\n", w->width, w->height, c->d, (int)w->w, (int)w->p);
XCopyArea(c->d, w->p, w->w, c->colors[1], 0, 0, w->width, w->height, 0, 0);
}
#ifndef _X_H_
#define _X_H_
/* public X interface */
#define BACKGROUND_COLOR 0
#define FOREGROUND_COLOR 1
typedef void x_connection;
typedef void x_window;
x_connection *x_open(void);
x_window *x_create_window(x_connection *x, int width, int height,
char *title);
int x_connection_fd(x_connection *x);
void x_flush(x_connection *x);
int x_new_color(x_connection *x, char *color);
/* for x_events, we pass the gui */
#include "gui.h"
void x_events(gui *gui);
void x_text_get_dimensions(x_connection *, const char *t,
int *width, int *height, int *baseline);
/* drawing functions */
void x_draw_line(x_connection *c, x_window *w, int color,
int x1, int y1, int x2, int y2);
void x_draw_rectangle(x_connection *c, x_window *w, int color,
int x, int y, int width, int height);
void x_fill_rectangle(x_connection *c, x_window *w, int color,
int x, int y, int width, int height);
void x_draw_string(x_connection *_c, x_window *_w, int color,
int x, int y, const char *t);
/* this function copies the pixmap to the window */
void x_draw(x_connection *c, x_window *w);
#endif /* _X_H_ */
#ifndef _X_DEFS_H_
#define _X_DEFS_H_
#include <X11/Xlib.h>
struct x_connection {
Display *d;
GC *colors;
int ncolors;
};
struct x_window {
Window w;
Pixmap p;
int width;
int height;
/* below: internal data used for X events handling */
int redraw;
int repaint;
int resize, new_width, new_height;
};
#endif /* _X_DEFS_H_ */
#include "gui.h"
#include "gui_defs.h"
#include "x.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
static void paint(gui *_gui, widget *_this)
{
struct gui *g = _gui;
struct xy_plot_widget *this = _this;
int wanted_plot_width, allocated_plot_width;
int wanted_plot_height, allocated_plot_height;
float pxsize;
float ticdist;
float tic;
float ticstep;
int k, kmin, kmax;
float allocated_xmin, allocated_xmax;
float allocated_ymin, allocated_ymax;
float center;
printf("PAINT xy plot xywh %d %d %d %d\n", this->common.x, this->common.y, this->common.width, this->common.height);
//x_draw_rectangle(g->x, g->xwin, 1, this->common.x, this->common.y, this->common.width, this->common.height);
/* plot zone */
/* TODO: refine height - height of hrule text may be != from label */
x_draw_rectangle(g->x, g->xwin, 1,
this->common.x + this->vrule_width,
this->common.y,
this->common.width - this->vrule_width -1, /* -1 to see right border */
this->common.height - this->label_height * 2);
/* horizontal tics */
wanted_plot_width = this->wanted_width;
allocated_plot_width = this->common.width - this->vrule_width;
pxsize = (this->xmax - this->xmin) / wanted_plot_width;
ticdist = 100;
tic = floor(log10(ticdist * pxsize));
ticstep = powf(10, tic);
center = (this->xmax + this->xmin) / 2;
allocated_xmin = center - ((this->xmax - this->xmin) *
allocated_plot_width / wanted_plot_width) / 2;
allocated_xmax = center + ((this->xmax - this->xmin) *
allocated_plot_width / wanted_plot_width) / 2;
/* adjust tic if too tight */
printf("pre x ticstep %g\n", ticstep);
while (1) {
if (ticstep / (allocated_xmax - allocated_xmin)
* (allocated_plot_width - 1) > 40) break;
ticstep *= 2;
}
printf("post x ticstep %g\n", ticstep);
printf("xmin/max %g %g width wanted allocated %d %d alloc xmin/max %g %g ticstep %g\n", this->xmin, this->xmax, wanted_plot_width, allocated_plot_width, allocated_xmin, allocated_xmax, ticstep);
kmin = ceil(allocated_xmin / ticstep);
kmax = floor(allocated_xmax / ticstep);
for (k = kmin; k <= kmax; k++) {
/*
(k * ticstep - allocated_xmin) / (allocated_max - allocated_xmin) =
(x - 0) / (allocated_plot_width-1 - 0)
*/
char v[64];
int vwidth, dummy;
float x = (k * ticstep - allocated_xmin) /
(allocated_xmax - allocated_xmin) *
(allocated_plot_width - 1);
x_draw_line(g->x, g->xwin, FOREGROUND_COLOR,
this->common.x + this->vrule_width + x,
this->common.y + this->common.height - this->label_height * 2,
this->common.x + this->vrule_width + x,
this->common.y + this->common.height - this->label_height * 2 - 5);
sprintf(v, "%g", k * ticstep);
x_text_get_dimensions(g->x, v, &vwidth, &dummy, &dummy);
x_draw_string(g->x, g->xwin, FOREGROUND_COLOR,
this->common.x + this->vrule_width + x - vwidth/2,
this->common.y + this->common.height - this->label_height * 2 + this->label_baseline,
v);
printf("tic k %d val %g x %g\n", k, k * ticstep, x);
}
/* vertical tics */
wanted_plot_height = this->wanted_height;
allocated_plot_height = this->common.height - this->label_height * 2;
pxsize = (this->ymax - this->ymin) / wanted_plot_height;
ticdist = 30;
tic = floor(log10(ticdist * pxsize));
ticstep = powf(10, tic);
center = (this->ymax + this->ymin) / 2;
allocated_ymin = center - ((this->ymax - this->ymin) *
allocated_plot_height / wanted_plot_height) / 2;
allocated_ymax = center + ((this->ymax - this->ymin) *
allocated_plot_height / wanted_plot_height) / 2;
/* adjust tic if too tight */
printf("pre y ticstep %g\n", ticstep);
while (1) {
if (ticstep / (allocated_ymax - allocated_ymin)
* (allocated_plot_height - 1) > 20) break;
ticstep *= 2;
}
printf("post y ticstep %g\n", ticstep);
printf("ymin/max %g %g height wanted allocated %d %d alloc ymin/max %g %g ticstep %g\n", this->ymin, this->ymax, wanted_plot_height, allocated_plot_height, allocated_ymin, allocated_ymax, ticstep);
kmin = ceil(allocated_ymin / ticstep);
kmax = floor(allocated_ymax / ticstep);
for (k = kmin; k <= kmax; k++) {
char v[64];
int vwidth, dummy;
float y = (k * ticstep - allocated_ymin) /
(allocated_ymax - allocated_ymin) *
(allocated_plot_height - 1);
sprintf(v, "%g", k * ticstep);
x_text_get_dimensions(g->x, v, &vwidth, &dummy, &dummy);
x_draw_line(g->x, g->xwin, FOREGROUND_COLOR,
this->common.x + this->vrule_width,
this->common.y + y,
this->common.x + this->vrule_width + 5,
this->common.y + y);
x_draw_string(g->x, g->xwin, FOREGROUND_COLOR,
this->common.x + this->vrule_width - vwidth - 2,
this->common.y + y - this->label_height / 2 + this->label_baseline,
v);
}
/* label at bottom, in the middle */
x_draw_string(g->x, g->xwin, FOREGROUND_COLOR,
this->common.x + (this->common.width - this->label_width) / 2,
this->common.y + this->common.height - this->label_height
+ this->label_baseline,
this->label);
}
static void hints(gui *_gui, widget *_w, int *width, int *height)
{
struct xy_plot_widget *w = _w;
*width = w->wanted_width + w->vrule_width;
*height = w->wanted_height + w->label_height * 2; /* TODO: refine */
printf("HINTS xy plot wh %d %d (vrule_width %d) (wanted wh %d %d)\n", *width, *height, w->vrule_width, w->wanted_width, w->wanted_height);
}
widget *new_xy_plot(gui *_gui, int width, int height, char *label,
int vruler_width)
{
struct gui *g = _gui;
struct xy_plot_widget *w;
glock(g);
w = new_widget(g, XY_PLOT, sizeof(struct xy_plot_widget));
w->label = strdup(label); if (w->label == NULL) OOM;
/* TODO: be sure calling X there is valid wrt "global model" (we are
* not in the "gui thread") */
x_text_get_dimensions(g->x, label, &w->label_width, &w->label_height,
&w->label_baseline);
printf("XY PLOT label wh %d %d\n", w->label_width, w->label_height);
w->wanted_width = width;
w->wanted_height = height;
w->vrule_width = vruler_width;
w->xmin = -1;
w->xmax = 1;
w->ymin = -1;
w->ymax = 1;
w->common.paint = paint;
w->common.hints = hints;
gunlock(g);
return w;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment