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