diff --git a/common/utils/T/tracer/Makefile b/common/utils/T/tracer/Makefile index 093acfbe3c380d7274b82a649022d6a860715644..fab5012c2862bc56499cdf425f147515700dfa67 100644 --- a/common/utils/T/tracer/Makefile +++ b/common/utils/T/tracer/Makefile @@ -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 diff --git a/common/utils/T/tracer/gui/Makefile b/common/utils/T/tracer/gui/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..13820114ddd69e0debe47f2f33aa4db0db98c22e --- /dev/null +++ b/common/utils/T/tracer/gui/Makefile @@ -0,0 +1,17 @@ +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 diff --git a/common/utils/T/tracer/gui/container.c b/common/utils/T/tracer/gui/container.c new file mode 100644 index 0000000000000000000000000000000000000000..23f7a42295dd6f3a0bccaa1262776d79ed06b885 --- /dev/null +++ b/common/utils/T/tracer/gui/container.c @@ -0,0 +1,206 @@ +#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; +} diff --git a/common/utils/T/tracer/gui/event.c b/common/utils/T/tracer/gui/event.c new file mode 100644 index 0000000000000000000000000000000000000000..fe7f9fbb47215c571115b8e3b9396f5d63c81d8f --- /dev/null +++ b/common/utils/T/tracer/gui/event.c @@ -0,0 +1,165 @@ +#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"); +} diff --git a/common/utils/T/tracer/gui/gui.c b/common/utils/T/tracer/gui/gui.c new file mode 100644 index 0000000000000000000000000000000000000000..55c7a3680aa04b23341cd2d6c123acf6e0a96cfe --- /dev/null +++ b/common/utils/T/tracer/gui/gui.c @@ -0,0 +1,24 @@ +#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); +} diff --git a/common/utils/T/tracer/gui/gui.h b/common/utils/T/tracer/gui/gui.h new file mode 100644 index 0000000000000000000000000000000000000000..846b16457d2e892be9c33716a83254b1e64a032b --- /dev/null +++ b/common/utils/T/tracer/gui/gui.h @@ -0,0 +1,36 @@ +#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_ */ diff --git a/common/utils/T/tracer/gui/gui_defs.h b/common/utils/T/tracer/gui/gui_defs.h new file mode 100644 index 0000000000000000000000000000000000000000..d7032f95daac0c1b0c0fced830d280864b36dd81 --- /dev/null +++ b/common/utils/T/tracer/gui/gui_defs.h @@ -0,0 +1,178 @@ +#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_ */ diff --git a/common/utils/T/tracer/gui/init.c b/common/utils/T/tracer/gui/init.c new file mode 100644 index 0000000000000000000000000000000000000000..c708639674f88099dc1cdaf7c6df6ff96fa0ed11 --- /dev/null +++ b/common/utils/T/tracer/gui/init.c @@ -0,0 +1,29 @@ +#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; +} diff --git a/common/utils/T/tracer/gui/label.c b/common/utils/T/tracer/gui/label.c new file mode 100644 index 0000000000000000000000000000000000000000..be6c8794a07af1354382221590f7762655e01c02 --- /dev/null +++ b/common/utils/T/tracer/gui/label.c @@ -0,0 +1,46 @@ +#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; +} diff --git a/common/utils/T/tracer/gui/loop.c b/common/utils/T/tracer/gui/loop.c new file mode 100644 index 0000000000000000000000000000000000000000..94b7fe54bd5f1cc436f8d05ebc7f6e51f639c464 --- /dev/null +++ b/common/utils/T/tracer/gui/loop.c @@ -0,0 +1,59 @@ +#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); + } +} diff --git a/common/utils/T/tracer/gui/test.c b/common/utils/T/tracer/gui/test.c new file mode 100644 index 0000000000000000000000000000000000000000..06e3b6309d2b81223675268ca9b92129573daf1d --- /dev/null +++ b/common/utils/T/tracer/gui/test.c @@ -0,0 +1,46 @@ +#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; +} diff --git a/common/utils/T/tracer/gui/text_list.c b/common/utils/T/tracer/gui/text_list.c new file mode 100644 index 0000000000000000000000000000000000000000..89b7e8822d8d427bee2f13bf65ba9f3d75c46a0a --- /dev/null +++ b/common/utils/T/tracer/gui/text_list.c @@ -0,0 +1,95 @@ +#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); +} diff --git a/common/utils/T/tracer/gui/toplevel_window.c b/common/utils/T/tracer/gui/toplevel_window.c new file mode 100644 index 0000000000000000000000000000000000000000..b1dd251f6b4bd981386e87ca650dea18fad7d60f --- /dev/null +++ b/common/utils/T/tracer/gui/toplevel_window.c @@ -0,0 +1,88 @@ +#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; +} diff --git a/common/utils/T/tracer/gui/widget.c b/common/utils/T/tracer/gui/widget.c new file mode 100644 index 0000000000000000000000000000000000000000..8304420aa13109598ca2fa95cbc049b4e31f6103 --- /dev/null +++ b/common/utils/T/tracer/gui/widget.c @@ -0,0 +1,216 @@ +#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; +} diff --git a/common/utils/T/tracer/gui/x.c b/common/utils/T/tracer/gui/x.c new file mode 100644 index 0000000000000000000000000000000000000000..b498654fdb4110a8aadac2f3f96969238cbcc716 --- /dev/null +++ b/common/utils/T/tracer/gui/x.c @@ -0,0 +1,284 @@ +#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); +} diff --git a/common/utils/T/tracer/gui/x.h b/common/utils/T/tracer/gui/x.h new file mode 100644 index 0000000000000000000000000000000000000000..41cf65393bcdeaafafa1c4413ca65727c69f04cb --- /dev/null +++ b/common/utils/T/tracer/gui/x.h @@ -0,0 +1,47 @@ +#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_ */ diff --git a/common/utils/T/tracer/gui/x_defs.h b/common/utils/T/tracer/gui/x_defs.h new file mode 100644 index 0000000000000000000000000000000000000000..43b99f7cf7ae907f2245b2f51ff9ac96b68b7f30 --- /dev/null +++ b/common/utils/T/tracer/gui/x_defs.h @@ -0,0 +1,23 @@ +#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_ */ diff --git a/common/utils/T/tracer/gui/xy_plot.c b/common/utils/T/tracer/gui/xy_plot.c new file mode 100644 index 0000000000000000000000000000000000000000..b2775fa054007c7537a6baba6d406c447761b4d5 --- /dev/null +++ b/common/utils/T/tracer/gui/xy_plot.c @@ -0,0 +1,173 @@ +#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; +}