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