Main pokey interface files.
[tinc] / src / pokey / interface.c
1 #include "config.h"
2
3 #include <stdio.h>
4 #include <string.h>
5 #include <sys/time.h>
6 #include <math.h>
7
8 #include <gtk/gtk.h>
9 #include <glade/glade.h>
10 #include <libgnomeui/gnome-canvas.h>
11 #include <libgnomeui/gnome-canvas-rect-ellipse.h>
12 #include <libgnomeui/gnome-canvas-text.h>
13 #include <libgnomeui/gnome-canvas-line.h>
14 #include <libgnomeui/gnome-canvas-util.h>
15
16 #include "node.h"
17 #include "edge.h"
18 #include "interface.h"
19
20 #include <xalloc.h>
21
22 #include "system.h"
23
24 extern GladeXML *xml;
25
26 #ifdef MAXBUFSIZE
27 #undef MAXBUFSIZE
28 #endif
29
30 #define MAXBUFSIZE 1024
31
32 int build_graph = 0;
33
34 static GdkColormap *colormap = NULL;
35 static GdkColor timecolor;
36
37 #define MAX_NODES 25
38 #define K 10.0
39
40 #ifdef INFINITY
41 #undef INFINITY
42 #endif
43 #define INFINITY 1.0e10
44
45 node_t *nodes[MAX_NODES];
46 double x[MAX_NODES];
47 double y[MAX_NODES];
48 double k[MAX_NODES][MAX_NODES];
49 double d[MAX_NODES][MAX_NODES];
50 double l[MAX_NODES][MAX_NODES];
51 const double epsilon = 0.001;
52
53 static int inited = 0;
54
55 static int number_of_nodes = 0;
56
57 static GtkWidget *nodetree;
58 static GtkCTreeNode *subnets_ctn, *hosts_ctn, *conns_ctn;
59
60 static GnomeCanvasGroup *edge_group = NULL;
61
62 static int canvas_width;
63 static int canvas_height;
64
65 static GtkWidget *canvas = NULL;
66
67 GtkWidget *create_canvas(void)
68 {
69   GtkWidget *w;
70
71   gtk_widget_push_visual(gdk_rgb_get_visual());
72   gtk_widget_push_colormap(gdk_rgb_get_cmap());
73   
74   canvas = gnome_canvas_new_aa();
75   
76   gtk_widget_pop_visual();
77   gtk_widget_pop_colormap();
78   
79   gnome_canvas_set_scroll_region(GNOME_CANVAS(canvas), 0, 0, 500, 300);
80   
81   w = glade_xml_get_widget(xml, "scrolledwindow3");
82   if(!w)
83     {
84       fprintf(stderr, "Could not find widget `scrolledwindow3'\n");
85       return NULL;
86     }
87   gtk_container_add(GTK_CONTAINER(w), canvas);
88   gtk_widget_show_all(w);
89
90   canvas_width = 300.0;
91   canvas_height = 500.0;
92
93   return canvas;
94 }
95
96 int init_interface(void)
97 {
98   char *l[1];
99   GtkArg p;
100
101   if(!xml)
102     return -1;
103
104   nodetree = glade_xml_get_widget(xml, "NodeTree");
105   if(!nodetree)
106     {
107       fprintf(stderr, _("Could not find widget `NodeTree'\n"));
108       return -1;
109     }
110
111   gtk_clist_freeze(GTK_CLIST(nodetree));
112
113   l[0] = _("Hosts");
114   hosts_ctn = gtk_ctree_insert_node(GTK_CTREE(nodetree),
115                               NULL, NULL, l, 1,
116                               NULL, NULL, NULL, NULL,
117                               FALSE, TRUE);
118   l[0] = _("Subnets");
119   subnets_ctn = gtk_ctree_insert_node(GTK_CTREE(nodetree),
120                               NULL, NULL, l, 1,
121                               NULL, NULL, NULL, NULL,
122                               FALSE, TRUE);
123   l[0] = _("Connections");
124   conns_ctn = gtk_ctree_insert_node(GTK_CTREE(nodetree),
125                               NULL, NULL, l, 1,
126                               NULL, NULL, NULL, NULL,
127                               FALSE, TRUE);
128   
129   gtk_clist_thaw(GTK_CLIST(nodetree));
130
131   create_canvas();
132   
133   return 0;
134 }
135
136 void log_message(int severity, const char *fmt, ...)
137 {
138   va_list args;
139   char buffer1[MAXBUFSIZE];
140   char buffer2[MAXBUFSIZE];
141   GtkWidget *w;
142   int len;
143   char *p;
144   struct tm *tm;
145   time_t t;
146   static int inited = 0;
147
148   if(!xml)
149     return;
150   
151   w = glade_xml_get_widget(xml, "Messages");
152   if(!w)
153     return;
154
155   /* Use vsnprintf instead of vasprintf: faster, no memory
156      fragmentation, cleanup is automatic, and there is a limit on the
157      input buffer anyway */
158   va_start(args, fmt);
159   len = vsnprintf(buffer1, MAXBUFSIZE, fmt, args);
160   va_end(args);
161
162   buffer1[MAXBUFSIZE-1] = '\0';
163   if((p = strrchr(buffer1, '\n')))
164     *p = '\0';
165
166   t = time(NULL);
167   tm = localtime(&t);
168   snprintf(buffer2, MAXBUFSIZE, "%02d:%02d:%02d ",
169            tm->tm_hour, tm->tm_min, tm->tm_sec);
170
171   if(!colormap)
172     {
173       colormap = gdk_colormap_new(gdk_visual_get_system(), FALSE);
174       timecolor.red = 0xffff;
175       timecolor.green = 0;
176       timecolor.blue = 0;
177       if(gdk_colormap_alloc_color(colormap, &timecolor, FALSE, TRUE) != TRUE)
178         {
179           fprintf(stderr, "Failed to allocate color\n");
180           exit(1);
181         }
182     }
183   
184   gtk_text_freeze(GTK_TEXT(w));
185
186   if(inited)
187     gtk_text_insert(GTK_TEXT(w), NULL, NULL, NULL, "\n", 1);
188
189   gtk_text_insert(GTK_TEXT(w), NULL, &timecolor, NULL, buffer2, strlen(buffer2));
190   gtk_text_insert(GTK_TEXT(w), NULL, NULL, NULL, buffer1, len);
191   gtk_text_thaw(GTK_TEXT(w));
192
193   inited = 1;
194 }
195
196 static gint item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data)
197 {
198   static double item_x, old_x, new_x, item_y, old_y, new_y;
199   static int dragging = FALSE;
200   GdkCursor *fleur;
201   node_t *n;
202   
203   item_x = event->button.x;
204   item_y = event->button.y;
205   gnome_canvas_item_w2i(item->parent, &item_x, &item_y);
206   
207   switch(event->type)
208     {
209     case GDK_BUTTON_PRESS:
210       switch(event->button.button)
211         {
212         case 1:
213           old_x = item_x;
214           old_y = item_y;
215
216           fleur = gdk_cursor_new(GDK_FLEUR);
217           gnome_canvas_item_grab(item, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, fleur, event->button.time);
218           gdk_cursor_destroy(fleur);
219           dragging = TRUE;
220           break;
221
222         default:
223           break;
224         }
225       break;
226
227     case GDK_MOTION_NOTIFY:
228       if(dragging && (event->motion.state & GDK_BUTTON1_MASK))
229         {
230           new_x = item_x,
231           new_y = item_y;
232           gnome_canvas_item_move(item, new_x - old_x, new_y - old_y);
233           old_x = new_x;
234           old_y = new_y;
235         }
236       break;
237       
238     case GDK_BUTTON_RELEASE:
239       gnome_canvas_item_ungrab(item, event->button.time);
240       dragging = FALSE;
241       n = (node_t *)gtk_object_get_user_data(GTK_OBJECT(item));
242       n->x = item_x;
243       n->y = item_y;
244       x[n->id] = item_x;
245       y[n->id] = item_y;
246       build_graph = 1;
247       break;
248
249     default:
250       break;
251     }
252   return FALSE;
253 }
254
255 GtkCTreeNode *if_node_add(node_t *n)
256 {
257   char *l[1];
258   GtkCTreeNode *ctn;
259
260   if(!xml)
261     return NULL;
262
263   l[0] = n->name;
264   gtk_clist_freeze(GTK_CLIST(nodetree));
265   n->host_ctn = gtk_ctree_insert_node(GTK_CTREE(nodetree),
266                                       hosts_ctn, NULL, l, 1,
267                                       NULL, NULL, NULL, NULL,
268                                       FALSE, FALSE);
269   gtk_clist_thaw(GTK_CLIST(nodetree));
270
271   if_graph_add_node(n);
272
273   inited = 0;
274   build_graph = 1;
275
276   return ctn;
277 }
278
279 void if_node_del(node_t *n)
280 {
281   gtk_clist_freeze(GTK_CLIST(nodetree));
282   if(n->host_ctn)
283     gtk_ctree_remove_node(GTK_CTREE(nodetree), n->host_ctn);
284   if(n->conn_ctn)
285     gtk_ctree_remove_node(GTK_CTREE(nodetree), n->conn_ctn);
286   if(n->subnet_ctn)
287     gtk_ctree_remove_node(GTK_CTREE(nodetree), n->subnet_ctn);
288   gtk_clist_thaw(GTK_CLIST(nodetree));
289 }
290
291 void if_subnet_add(subnet_t *subnet)
292 {
293   char *l[1];
294   
295   l[0] = net2str(subnet);
296   gtk_clist_freeze(GTK_CLIST(nodetree));
297   gtk_ctree_insert_node(GTK_CTREE(nodetree),
298                         subnets_ctn, NULL, l, 1,
299                         NULL, NULL, NULL, NULL,
300                         TRUE, FALSE);
301   gtk_clist_thaw(GTK_CLIST(nodetree));
302 }
303
304 void if_subnet_del(subnet_t *subnet)
305 {
306 }
307
308 void redraw_edges(void)
309 {
310   GnomeCanvasGroup *group;
311   GnomeCanvasPoints *points;
312   avl_node_t *avlnode;
313   edge_t *e;
314
315   if(edge_group)
316     gtk_object_destroy(GTK_OBJECT(edge_group));
317   
318   group = gnome_canvas_root(GNOME_CANVAS(canvas));
319   group = GNOME_CANVAS_GROUP(gnome_canvas_item_new(group,
320                                                    gnome_canvas_group_get_type(),
321                                                    "x", 0.0,
322                                                    "y", 0.0,
323                                                    NULL));
324   
325   for(avlnode = edge_tree->head; avlnode; avlnode = avlnode->next)
326     {
327 /*       char s[12]; */
328       e = (edge_t *)avlnode->data;
329       
330       points = gnome_canvas_points_new(2);
331       
332       points->coords[0] = e->from.node->x;
333       points->coords[1] = e->from.node->y;
334       points->coords[2] = e->to.node->x;
335       points->coords[3] = e->to.node->y;
336       gnome_canvas_item_new(group,
337                             gnome_canvas_line_get_type(),
338                             "points", points,
339                             "fill_color_rgba", 0xe080c0ff,
340                             "width_pixels", 2,
341                             NULL);
342       gnome_canvas_points_unref(points);
343 /*       snprintf(s, sizeof(s) - 1, "%d", e->weight); */
344 /*       gnome_canvas_item_new(group, */
345 /*                          gnome_canvas_text_get_type(), */
346 /*                          "x", (e->from.node->x + e->to.node->x) / 2, */
347 /*                          "y", (e->from.node->y + e->to.node->y) / 2, */
348 /*                          "text", s, */
349 /*                          "anchor", GTK_ANCHOR_CENTER, */
350 /*                          "fill_color", "black", */
351 /*                          "font", "-*-verdana-medium-r-*-*-8-*-*-*-*-*-iso8859-1", */
352 /*                          /\*                         "font", "fixed", *\/ */
353 /*                          NULL); */
354     }
355
356   edge_group = group;
357 }
358
359 void if_edge_add(edge_t *e)
360 {
361   redraw_edges();
362   if_graph_add_edge(e);
363   inited = 0;
364   build_graph = 1;
365
366 }
367
368 void if_edge_del(edge_t *e)
369 {
370   redraw_edges();
371   inited = 0;
372   build_graph = 1;
373
374 }
375
376 void if_graph_add_node(node_t *n)
377 {
378   GnomeCanvasGroup *group;
379   double newx, newy;
380   
381   if(!canvas)
382     if(!create_canvas())
383       return;
384   
385   group = gnome_canvas_root(GNOME_CANVAS(canvas));
386   group = GNOME_CANVAS_GROUP(gnome_canvas_item_new(group,
387                                                    gnome_canvas_group_get_type(),
388                                                    "x", 0.0,
389                                                    "y", 0.0,
390                                                    NULL));
391   
392   gnome_canvas_item_new(group, gnome_canvas_ellipse_get_type(),
393                         "x1", -30.0,
394                         "y1", -08.0,
395                         "x2", 30.0,
396                         "y2", 08.0,
397                         "fill_color_rgba", 0x5f9ea0ff,
398                         "outline_color", "black",
399                         "width_pixels", 0,
400                         NULL);
401   
402   gnome_canvas_item_new(group,
403                         gnome_canvas_text_get_type(),
404                         "x", 0.0,
405                         "y", 0.0,
406                         "text", n->name,
407                         "anchor", GTK_ANCHOR_CENTER,
408                         "fill_color", "white",
409                         "font", "-*-verdana-medium-r-*-*-10-*-*-*-*-*-iso8859-1",
410 /*                      "font", "fixed", */
411                         NULL);
412   
413   n->item = GNOME_CANVAS_ITEM(group);
414   gtk_object_set_user_data(GTK_OBJECT(group), (gpointer)n);
415   
416   /* TODO: Use this to get more detailed info on a node (For example
417      popup a dialog with more info, select the node in the left
418      pane, whatever.) */
419   gtk_signal_connect(GTK_OBJECT(n->item), "event", (GtkSignalFunc) item_event, NULL);
420
421   newx = 250.0 + 200.0 * sin(number_of_nodes / 10.0 * M_PI);
422   newy = 150.0 - 100.0 * cos(number_of_nodes / 10.0 * M_PI);
423 /*   newx = (double)random() / (double)RAND_MAX * 300.0 + 100.0; */
424 /*   newy = (double)random() / (double)RAND_MAX * 200.0 + 50.0; */
425   gnome_canvas_item_move(GNOME_CANVAS_ITEM(n->item), newx, newy);
426   n->x = newx;
427   n->y = newy;
428
429   x[number_of_nodes] = newx;
430   y[number_of_nodes] = newy;
431   nodes[number_of_nodes] = n;
432   n->id = number_of_nodes;
433   
434   number_of_nodes++;
435
436   gnome_canvas_update_now(GNOME_CANVAS(canvas));
437 }
438
439 void if_move_node(node_t *n, double dx, double dy)
440 {
441   double newx, newy;
442   
443   newx = n->x + dx;
444   newy = n->y + dy;
445   gnome_canvas_item_move(GNOME_CANVAS_ITEM(n->item), newx - n->x, newy - n->y);
446   n->x = newx;
447   n->y = newy;
448 }
449
450 void if_graph_add_edge(edge_t *e)
451 {
452 /*   e->from.node->ifn->nat++; */
453 /*   e->to.node->ifn->nat++; */
454
455 /*   avl_insert(e->from.node->ifn->attractors, e->to.node); */
456 /*   avl_insert(e->to.node->ifn->attractors, e->from.node); */
457
458   redraw_edges();
459
460   build_graph = 1;
461 }
462
463 #define X_MARGIN 50.0
464 #define X_MARGIN_BUFFER 25.0
465 #define Y_MARGIN 20.0
466 #define Y_MARGIN_BUFFER 10.0
467
468 void set_zooming(void)
469 {
470   int i;
471   double minx, miny, maxx, maxy;
472   static double ominx = 0.0, ominy = 0.0, omaxx = 0.0, omaxy = 0.0;
473   double ppu, ppux, ppuy;
474
475   minx = miny = maxx = maxy = 0.0;
476   for(i = 0; i < number_of_nodes; i++)
477     {
478       if(nodes[i]->x < minx)
479         minx = nodes[i]->x;
480       else
481         if(nodes[i]->x > maxx)
482           maxx = nodes[i]->x;
483
484       if(nodes[i]->y < miny)
485         miny = nodes[i]->y;
486       else
487         if(nodes[i]->y > maxy)
488           maxy = nodes[i]->y;
489     }
490
491   if(minx > ominx - X_MARGIN_BUFFER && ominx > minx)
492     minx = ominx;
493   if(maxx < omaxx + X_MARGIN_BUFFER && omaxx < maxx)
494     maxx = omaxx;
495   if(miny > ominy - Y_MARGIN_BUFFER && ominy > miny)
496     miny = ominy;
497   if(maxy < omaxy + Y_MARGIN_BUFFER && omaxy < maxy)
498     maxy = omaxy;
499
500   ominx = minx; ominy = miny; omaxx = maxx; omaxy = maxy;
501
502 /*   ppux = canvas_width / (maxx - minx); */
503 /*   ppuy = canvas_height / (maxy - miny); */
504 /*   if(ppux < ppuy) */
505 /*     ppu = ppux; */
506 /*   else */
507 /*     ppu = ppuy; */
508
509 /*   gnome_canvas_set_pixels_per_unit(GNOME_CANVAS(canvas), ppu); */
510   gnome_canvas_set_scroll_region(GNOME_CANVAS(canvas), minx - X_MARGIN, miny - Y_MARGIN, maxx + X_MARGIN, maxy + Y_MARGIN);
511 }
512
513 double calculate_delta_m(int m)
514 {
515   double dedxm, dedym, xmxi, ymyi;
516   int i;
517
518   dedxm = dedym = 0.0;
519   for(i = 0; i < number_of_nodes; i++)
520     {
521       if(i == m)
522         continue;
523
524       xmxi = x[m] - x[i];
525       ymyi = y[m] - y[i];
526
527       dedxm += k[m][i] * (xmxi - ((l[m][i] * xmxi) / sqrt(xmxi * xmxi + ymyi * ymyi)));
528       dedym += k[m][i] * (xmxi - ((l[m][i] * xmxi) / sqrt(xmxi * xmxi + ymyi * ymyi)));
529     }
530
531   return sqrt(dedxm * dedxm + dedym * dedym);
532 }
533
534 void move_node(int m, double *dx, double *dy)
535 {
536   double d2edxm2, d2edym2, d2edxmdym, dedxm, dedym;
537   double xmxi, ymyi, denominator;
538   int i;
539
540   d2edxm2 = d2edym2 = d2edxmdym = dedxm = dedym = 0.0;
541   for(i = 0; i < number_of_nodes; i++)
542     {
543       if(i == m)
544         continue;
545       
546       xmxi = x[m] - x[i];
547       ymyi = y[m] - y[i];
548
549       denominator = pow(sqrt(xmxi * xmxi + ymyi * ymyi), 3.0);
550
551       d2edxm2 += k[m][i] * (1 - ((l[m][i] * ymyi * ymyi) / denominator));
552       d2edxmdym += k[m][i] * l[m][i] * xmxi * ymyi / denominator;
553       d2edym2 += k[m][i] * (1 - ((l[m][i] * xmxi * xmxi) / denominator));
554       dedxm += k[m][i] * (xmxi - ((l[m][i] * xmxi) / sqrt(xmxi * xmxi + ymyi * ymyi)));
555       dedym += k[m][i] * (ymyi - ((l[m][i] * ymyi) / sqrt(xmxi * xmxi + ymyi * ymyi)));
556     }
557
558   denominator = ((d2edxm2 * d2edym2) - (d2edxmdym * d2edxmdym));
559   *dx = (-(d2edym2 * dedxm) + (d2edxmdym * dedym)) / denominator;
560   *dy = ((d2edxmdym * dedxm) - (d2edxm2 * dedym)) / denominator;
561 }
562
563 void if_build_graph(void)
564 {
565   int i, j, p, max_i;
566   double delta_m, max_delta_m;
567   double dx, dy, s, L, max_d, old_x, old_y;
568   edge_t *e;
569
570   if(!inited)
571     {
572       for(i = 0; i < number_of_nodes; i++)
573         {
574           d[i][i] = 0.0;
575           for(j = i + 1; j < number_of_nodes; j++)
576             {
577               e = lookup_edge(nodes[i], nodes[j]);
578               if(e)
579                 d[i][j] = d[j][i] = (double)e->weight;
580               else
581                 d[i][j] = d[j][i] = INFINITY;
582             }
583         }
584       for(i = 0; i < number_of_nodes; i++)
585         {
586           for(j = 0; j < number_of_nodes; j++)
587             {
588               if(i == j)
589                 continue;
590               
591               if(d[j][i] < INFINITY)
592                 {
593                   for(p = 0; p < number_of_nodes; p++)
594                     {
595                       if(d[i][j] < INFINITY)
596                         {
597                           s = d[j][i] + d[i][p];
598                           if(s < d[j][p])
599                             {
600                               d[j][p] = s;
601                             }
602                         }
603                     }
604                 }
605             }
606         }
607
608       max_d = 0.0;
609       for(i = 0; i < number_of_nodes; i++)
610         for(j = i + 1; j < number_of_nodes; j++)
611           if(d[i][j] > max_d && d[i][j] < INFINITY)
612             max_d = d[i][j];
613
614       L = 300.0 / log(max_d);
615
616       for(i = 0; i < number_of_nodes; i++)
617         {
618           for(j = i + 1; j < number_of_nodes; j++)
619             {
620               d[i][j] = d[j][i] = log(d[i][j]+1.0);
621               l[i][j] = l[j][i] = L * d[i][j];
622               k[i][j] = k[j][i] = K / (d[i][j] * d[i][j]);
623             }
624         }
625
626       inited = 1;
627     }
628
629   max_delta_m = 0.0;
630   /* Find node with maximal local energy */
631   for(i = 0; i < number_of_nodes; i++)
632     {
633       delta_m = calculate_delta_m(i);
634       if(delta_m > max_delta_m)
635         {
636           max_delta_m = delta_m;
637           max_i = i;
638         }
639     }
640
641   if(max_delta_m <= epsilon)
642     build_graph = 0;
643   else
644     {
645       int iter = 0, maxiter = 20;
646       delta_m = max_delta_m;
647       old_x = x[max_i];
648       old_y = y[max_i];
649       while(delta_m > epsilon && iter < maxiter)
650         {
651           move_node(max_i, &dx, &dy);
652           x[max_i] += dx;
653           y[max_i] += dy;
654           delta_m = calculate_delta_m(max_i);
655           iter++;
656         }
657           
658       if_move_node(nodes[max_i], x[max_i] - old_x, y[max_i] - old_y);
659           
660       redraw_edges();
661
662       set_zooming();
663     }
664
665 /*   build_graph = 0; */
666 }