diff --git a/common/utils/T/tracer/gui/gui.h b/common/utils/T/tracer/gui/gui.h
index 43b955f91a566a1f039f6c67741d9026a6ad6036..0fb96db325798e77de38bee3af2fa9b1e62f9e7d 100644
--- a/common/utils/T/tracer/gui/gui.h
+++ b/common/utils/T/tracer/gui/gui.h
@@ -14,6 +14,10 @@ typedef void widget;
 
 #define DEFAULT_FONT 0
 
+/* tic type for XY plot */
+#define XY_PLOT_DEFAULT_TICK 0
+#define XY_PLOT_SCROLL_TICK  1
+
 /* key modifiers */
 #define KEY_SHIFT   (1<<0)
 #define KEY_CONTROL (1<<1)
@@ -54,6 +58,7 @@ void xy_plot_set_points(gui *gui, widget *this,
     int plot, int npoints, float *x, float *y);
 void xy_plot_get_dimensions(gui *gui, widget *this, int *width, int *height);
 void xy_plot_set_title(gui *gui, widget *this, char *label);
+void xy_plot_set_tick_type(gui *gui, widget *this, int type);
 
 void textlist_add(gui *gui, widget *this, const char *text, int position,
     int color);
diff --git a/common/utils/T/tracer/gui/gui_defs.h b/common/utils/T/tracer/gui/gui_defs.h
index 9cb94dc98a4f1104082d38b85619d1d055fa1fb0..88b8121ec9591b461d59ba1ae7c87f9deaa79d39 100644
--- a/common/utils/T/tracer/gui/gui_defs.h
+++ b/common/utils/T/tracer/gui/gui_defs.h
@@ -117,6 +117,7 @@ struct xy_plot_widget {
   int wanted_height;
   struct xy_plot_plot *plots;
   int nplots;
+  int tick_type;
 };
 
 struct timeline_subline {
diff --git a/common/utils/T/tracer/gui/xy_plot.c b/common/utils/T/tracer/gui/xy_plot.c
index 3baac92c04615dc61ab9c8327103abf26bd58d0c..4115f92103adca50bad18cd022dce6cbbc6b2680 100644
--- a/common/utils/T/tracer/gui/xy_plot.c
+++ b/common/utils/T/tracer/gui/xy_plot.c
@@ -1,11 +1,20 @@
 #include "gui.h"
 #include "gui_defs.h"
 #include "x.h"
+#include "../utils.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <math.h>
 
+#if 0
+/* this version behaves differently when you resize the XY plot. Say
+ * you increase the size. The old (smaller) view is put at the center
+ * of the new view. Everything inside keeps the same size/aspect ratio.
+ * The other version below resizes the old view so that it fully occupies
+ * the new view. It may introduce aspect ratio changes, but usage seems
+ * to suggest it's a better behaviour.
+ */
 static void paint(gui *_gui, widget *_this)
 {
   struct gui *g = _gui;
@@ -155,6 +164,184 @@ static void paint(gui *_gui, widget *_this)
     x_plot_points(g->x, g->xwin, this->plots[n].color);
   }
 }
+#endif
+
+static void paint(gui *_gui, widget *_this)
+{
+  struct gui *g = _gui;
+  struct xy_plot_widget *this = _this;
+  int allocated_plot_width;
+  int 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;
+  int i;
+  int n;
+  char v[64];
+  int vwidth, dummy;
+
+# define FLIP(v) (-(v) + allocated_plot_height-1)
+
+  LOGD("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);
+
+  allocated_plot_width = this->common.width - this->vrule_width;
+  allocated_plot_height = this->common.height - this->label_height * 2;
+
+  if (allocated_plot_width <= 1 || allocated_plot_height <= 1) {
+    WARN("PAINT xy plot: width (%d) or height (%d) is wrong, not painting\n",
+         this->common.width, this->common.height);
+    return;
+  }
+
+  /* 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 */
+  pxsize = (this->xmax - this->xmin) / allocated_plot_width;
+  ticdist = 100;
+  tic = floor(log10(ticdist * pxsize));
+  ticstep = powf(10, tic);
+  allocated_xmin = this->xmin;
+  allocated_xmax = this->xmax;
+  /* adjust tic if too tight */
+  LOGD("pre x ticstep %g\n", ticstep);
+  while (1) {
+    if (ticstep / (allocated_xmax - allocated_xmin)
+                * (allocated_plot_width - 1) > 40) break;
+    ticstep *= 2;
+  }
+  LOGD("post x ticstep %g\n", ticstep);
+  LOGD("xmin/max %g %g width allocated %d alloc xmin/max %g %g ticstep %g\n", this->xmin, this->xmax, 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)
+ */
+    int x = (k * ticstep - allocated_xmin) /
+            (allocated_xmax - allocated_xmin) *
+            (allocated_plot_width - 1);
+    int px;
+    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, DEFAULT_FONT, v, &vwidth, &dummy, &dummy);
+
+    /* do not draw after the widget ('width-1' for if we use 'width'
+     * it is still off by 1 pixel for whatever reason) */
+    px = this->vrule_width + x - vwidth/2;
+    if (px + vwidth > this->common.width-1)
+      px = this->common.width-1 - vwidth;
+
+    x_draw_string(g->x, g->xwin, DEFAULT_FONT, FOREGROUND_COLOR,
+        this->common.x + px,
+        this->common.y + this->common.height - this->label_height * 2 +
+            this->label_baseline,
+        v);
+    LOGD("tic k %d val %g x %d\n", k, k * ticstep, x);
+  }
+
+  /* vertical tics */
+  allocated_ymin = this->ymin;
+  allocated_ymax = this->ymax;
+  switch (this->tick_type) {
+  case XY_PLOT_DEFAULT_TICK:
+    pxsize = (this->ymax - this->ymin) / allocated_plot_height;
+    ticdist = 30;
+    tic = floor(log10(ticdist * pxsize));
+    ticstep = powf(10, tic);
+    /* adjust tic if too tight */
+    LOGD("pre y ticstep %g\n", ticstep);
+    while (1) {
+      if (ticstep / (allocated_ymax - allocated_ymin)
+                  * (allocated_plot_height - 1) > 20) break;
+      ticstep *= 2;
+    }
+    LOGD("post y ticstep %g\n", ticstep);
+    LOGD("ymin/max %g %g height allocated %d alloc "
+         "ymin/max %g %g ticstep %g\n", this->ymin, this->ymax,
+         allocated_plot_height, allocated_ymin, allocated_ymax, ticstep);
+    kmin = ceil(allocated_ymin / ticstep);
+    kmax = floor(allocated_ymax / ticstep);
+    for (k = kmin; k <= kmax; k++) {
+      int y = (k * ticstep - allocated_ymin) /
+              (allocated_ymax - allocated_ymin) *
+              (allocated_plot_height - 1);
+      sprintf(v, "%g", k * ticstep);
+      x_text_get_dimensions(g->x, DEFAULT_FONT, v, &vwidth, &dummy, &dummy);
+      x_draw_line(g->x, g->xwin, FOREGROUND_COLOR,
+          this->common.x + this->vrule_width,
+          this->common.y + FLIP(y),
+          this->common.x + this->vrule_width + 5,
+          this->common.y + FLIP(y));
+      x_draw_string(g->x, g->xwin, DEFAULT_FONT, FOREGROUND_COLOR,
+          this->common.x + this->vrule_width - vwidth - 2,
+          this->common.y + FLIP(y)-this->label_height/2+this->label_baseline,
+          v);
+    }
+    break;
+  case XY_PLOT_SCROLL_TICK:
+    /* print only max value, formatted using 'bps' for readability */
+    bps(v, this->ymax, "");
+    x_text_get_dimensions(g->x, DEFAULT_FONT, v, &vwidth, &dummy, &dummy);
+    x_draw_string(g->x, g->xwin, DEFAULT_FONT, FOREGROUND_COLOR,
+        this->common.x + this->vrule_width - vwidth - 2,
+        this->common.y + +this->label_baseline,
+        v);
+    /* vertically divide into 5 */
+    for (k = 1; k < 5; k++) {
+      int y = round((k * (allocated_plot_height - 1)) / 5.);
+      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);
+    }
+    break;
+  } /* swich (this->tick_type) */
+
+  /* label at bottom, in the middle */
+  x_draw_string(g->x, g->xwin, DEFAULT_FONT, 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);
+
+  for (n = 0; n < this->nplots; n++) {
+    /* points */
+    float ax, bx, ay, by;
+    ax = (allocated_plot_width-1) / (allocated_xmax - allocated_xmin);
+    bx = -ax * allocated_xmin;
+    ay = (allocated_plot_height-1) / (allocated_ymax - allocated_ymin);
+    by = -ay * allocated_ymin;
+    for (i = 0; i < this->plots[n].npoints; i++) {
+      int x, y;
+      x = ax * this->plots[n].x[i] + bx;
+      y = ay * this->plots[n].y[i] + by;
+      if (x >= 0 && x < allocated_plot_width &&
+          y >= 0 && y < allocated_plot_height)
+        x_add_point(g->x,
+            this->common.x + this->vrule_width + x,
+            this->common.y + FLIP(y));
+    }
+    x_plot_points(g->x, g->xwin, this->plots[n].color);
+  }
+}
 
 static void hints(gui *_gui, widget *_w, int *width, int *height)
 {
@@ -191,6 +378,7 @@ widget *new_xy_plot(gui *_gui, int width, int height, char *label,
   w->ymax = 1;
   w->plots = NULL;
   w->nplots = 0;
+  w->tick_type = XY_PLOT_DEFAULT_TICK;
 
   w->common.paint = paint;
   w->common.hints = hints;
@@ -309,3 +497,17 @@ void xy_plot_set_title(gui *_gui, widget *_this, char *label)
 
   gunlock(g);
 }
+
+void xy_plot_set_tick_type(gui *_gui, widget *_this, int type)
+{
+  struct gui *g = _gui;
+  struct xy_plot_widget *this = _this;
+
+  glock(g);
+
+  this->tick_type = type;
+
+  send_event(g, DIRTY, this->common.id);
+
+  gunlock(g);
+}