"Fossies" - the Fresh Open Source Software Archive

Member "etherape-0.9.18/src/diagram.c" (3 Jun 2018, 59694 Bytes) of package /linux/privat/etherape-0.9.18.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "diagram.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.9.17_vs_0.9.18.

    1 /* EtherApe
    2  * Copyright (C) 2001 Juan Toledo, Riccardo Ghetta
    3  *
    4  * This program is free software; you can redistribute it and/or modify
    5  * it under the terms of the GNU General Public License as published by
    6  * the Free Software Foundation; either version 2 of the License, or
    7  * (at your option) any later version.
    8  *
    9  * This program is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  *
   14  * You should have received a copy of the GNU General Public License
   15  * along with this program; if not, see <http://www.gnu.org/licenses/>.
   16  */
   17 
   18 #ifdef HAVE_CONFIG_H
   19 #include <config.h>
   20 #endif
   21 
   22 #include <gtk/gtk.h>
   23 #include <goocanvas.h>
   24 
   25 #include "appdata.h"
   26 #include "diagram.h"
   27 #include "pref_dialog.h"
   28 #include "stats/node.h"
   29 #include "info_windows.h"
   30 #include "stats/protocols.h"
   31 #include "datastructs.h"
   32 #include "names/ip-cache.h"
   33 #include "menus.h"
   34 #include "capture/capctl.h"
   35 #include "stats/conversations.h"
   36 #include "preferences.h"
   37 #include "export.h"
   38 #include "stats/util.h"
   39 
   40 /* maximum node and link size */
   41 #define MAX_NODE_SIZE 5000
   42 #define MAX_LINK_SIZE (MAX_NODE_SIZE/4)
   43 
   44 gboolean already_updating;
   45 gboolean stop_requested;
   46 
   47 /***************************************************************************
   48  *
   49  * canvas_node_t definition and methods
   50  *
   51  **************************************************************************/
   52 typedef struct
   53 {
   54   node_id_t canvas_node_id;
   55   GooCanvasItem *node_item;
   56   GooCanvasItem *text_item;
   57   GooCanvasGroup *group_item;
   58   GdkRGBA color;
   59   gboolean is_new;
   60   gboolean shown;       /* True if it is to be displayed. */
   61   gboolean centered;            /* true if is a center node */
   62 
   63   /* For '-P' mode (columnar layout) */
   64   guint column; /* Which column this goes in */
   65   guint column_idx; /* Which position within its column this node is */
   66 }
   67 canvas_node_t;
   68 
   69 static gint canvas_node_compare(const node_id_t *a, const node_id_t *b,
   70                                 gpointer dummy);
   71 static void canvas_node_delete(canvas_node_t *cn);
   72 static gint canvas_node_update(node_id_t  * ether_addr,
   73                  canvas_node_t * canvas_node,
   74                  GList ** delete_list);
   75 
   76 /***************************************************************************
   77  *
   78  * canvas_link_t definition and methods
   79  *
   80  **************************************************************************/
   81 typedef struct
   82 {
   83   link_id_t canvas_link_id; /* id of the link */
   84   GooCanvasItem *src_item;    /* triangle for src side */
   85   GooCanvasItem *dst_item;    /* triangle for dst side */
   86   GdkRGBA color;
   87 }
   88 canvas_link_t;
   89 static gint canvas_link_compare(const link_id_t *a, const link_id_t *b,
   90                                 gpointer dummy);
   91 static void canvas_link_delete(canvas_link_t *canvas_link);
   92 static gint canvas_link_update(link_id_t * link_id,
   93                  canvas_link_t * canvas_link,
   94                  GList ** delete_list);
   95 
   96 struct node_ring
   97 {
   98   gfloat angle;
   99   guint node_i;
  100   guint n_nodes;
  101 };
  102 
  103 typedef struct
  104 {
  105   GooCanvas * canvas;
  106 
  107   struct node_ring outer;
  108   struct node_ring center;
  109 
  110   gdouble xmin;
  111   gdouble ymin;
  112   gdouble xmax;
  113   gdouble ymax;
  114   gdouble x_rad_max;
  115   gdouble y_rad_max;
  116   gdouble x_inner_rad_max;
  117   gdouble y_inner_rad_max;
  118 
  119   guint *column_populations;
  120 } reposition_node_t;
  121 
  122 /***************************************************************************
  123  *
  124  * canvas_background_t definition and methods
  125  *
  126  **************************************************************************/
  127 
  128 typedef struct
  129 {
  130   gboolean use_image;
  131 
  132   struct
  133   {
  134     GdkPixbuf *image;
  135     gchar *path;
  136     GooCanvasItem *item;
  137   } image;
  138 } canvas_background_t;
  139 
  140 static void init_canvas_background(GooCanvasItem *rootitem);
  141 
  142 /***************************************************************************
  143  *
  144  * local variables
  145  *
  146  **************************************************************************/
  147 
  148 static GTree *canvas_nodes = NULL;  /* We don't use the nodes tree directly in order to
  149                  * separate data from presentation: that is, we need to
  150                  * keep a list of CanvasItems, but we do not want to keep
  151                  * that info on the nodes tree itself */
  152 static GTree *canvas_links; /* See canvas_nodes */
  153 static guint known_protocols = 0;
  154 static canvas_background_t canvas_background;
  155 static guint displayed_nodes;
  156 static gboolean need_reposition = TRUE; /* Force a diagram relayout */
  157 static gboolean need_font_refresh = TRUE;/* Force font refresh during layout */
  158 static gint diagram_timeout = 0;    /* Descriptor of the diagram timeout function
  159                         * (Used to change the refresh_period in the callback */
  160 
  161 static long canvas_obj_count = 0; /* counter of canvas objects */
  162 static GooCanvas *gcanvas_ = NULL; /* drawing canvas */
  163 static GooCanvasText *pcap_stats_text_item = NULL;
  164  
  165 /***************************************************************************
  166  *
  167  * local Function definitions
  168  *
  169  **************************************************************************/
  170 static gboolean update_diagram(GooCanvas *canvas); /* full diagram update */
  171 static void diagram_update_nodes(GooCanvas *canvas); /* updates ALL nodes */
  172 static void diagram_update_links(GooCanvas *canvas); /* updates ALL links */
  173 static void diagram_update_background_image(GooCanvas *canvas); /* updates background image */
  174 static void diagram_reposition(); /* reposition nodes */
  175 
  176 static void check_new_protocol(GtkWidget *prot_table, const protostack_t *pstk);
  177 static gint check_new_node(node_t *node, GooCanvas *canvas);
  178 static gboolean display_node(node_t *node);
  179 static void limit_nodes(void);
  180 static gint add_ordered_node(node_id_t *node_id,
  181                              canvas_node_t *canvas_node,
  182                              GTree *ordered_nodes);
  183 static gint check_ordered_node(gdouble *traffic, canvas_node_t *node,
  184                                guint *count);
  185 static gint traffic_compare(gconstpointer a, gconstpointer b);
  186 static gint reposition_canvas_nodes(node_id_t *node_id,
  187                                     canvas_node_t *canvas_node,
  188                                     reposition_node_t *data);
  189 static gint reposition_canvas_nodes_prep(const node_id_t *node_id,
  190                                          canvas_node_t *canvas_node,
  191                                          reposition_node_t *data);
  192 static gint check_new_link(link_id_t *link_id,
  193                            link_t *link,
  194                            GooCanvas *canvas);
  195 static gdouble get_node_size(gdouble average);
  196 static gdouble get_link_size(const basic_stats_t *link_stats);
  197 static gint pcap_stats_text_item_event(GooCanvasItem *item, GdkEvent *event,
  198                                        void *unused);
  199 static gboolean link_item_event(GooCanvasItem *item,
  200                                 GooCanvasItem *target_item,
  201                                 GdkEventButton *event,
  202                                 canvas_link_t *canvas_link);
  203 static gboolean node_item_event(GooCanvasItem *item,
  204                                 GooCanvasItem *target_item,
  205                                 GdkEventButton *event,
  206                                 canvas_node_t *canvas_node);
  207 
  208 static void update_legend(void);
  209 static void draw_oneside_link(double xs, double ys, double xd, double yd,
  210                               const basic_stats_t *link_data,
  211                               const GdkRGBA *scaledColor, GooCanvasItem *item);
  212 static void init_reposition(reposition_node_t *data,
  213                             GooCanvas *canvas,
  214                             guint total_nodes);
  215 static void clear_reposition(reposition_node_t *rdata);
  216 static void redraw_canvas_background(GooCanvas *canvas);
  217 static gboolean diagram_resize_event(GtkWidget *widget,
  218                                      const GdkEventConfigure *event,
  219                                      GooCanvas *canvas);
  220 
  221 /***************************************************************************
  222  *
  223  * implementation
  224  *
  225  **************************************************************************/
  226 GtkWidget *canvas_widget()
  227 {
  228    return GTK_WIDGET(gcanvas_);
  229 }
  230 
  231 static void goo_canvas_item_show(GooCanvasItem *it)
  232 {
  233   g_object_set(G_OBJECT (it),
  234                "visibility", GOO_CANVAS_ITEM_VISIBLE, 
  235                NULL);
  236 }
  237 static void goo_canvas_item_hide(GooCanvasItem *it)
  238 {
  239   g_object_set(G_OBJECT (it),
  240                "visibility", GOO_CANVAS_ITEM_INVISIBLE, 
  241                NULL);
  242 }
  243 
  244 void ask_reposition(gboolean r_font)
  245 {
  246   need_reposition = TRUE;
  247   need_font_refresh = r_font;
  248 }
  249 
  250 void dump_stats(guint32 diff_msecs)
  251 {
  252   gchar *status_string;
  253   long ipc=ipcache_active_entries();
  254   status_string = g_strdup_printf (
  255     _("Nodes: %d (on canvas: %d, shown: %u), Links: %d, Conversations: %ld, "
  256       "names %ld, protocols %ld. Total Packets seen: %lu (in memory: %ld, "
  257       "on list %ld). IP cache entries %ld. Canvas objs: %ld. Refreshed: %u ms"),
  258                                    node_count(),
  259                                    g_tree_nnodes(canvas_nodes), displayed_nodes,
  260                                    links_catalog_size(), active_conversations(),
  261                                    active_names(), protocol_summary_size(),
  262                                    appdata.n_packets, appdata.total_mem_packets,
  263                                    packet_list_item_count(), ipc,
  264                                    canvas_obj_count,
  265                                    (unsigned int) diff_msecs);
  266 
  267   g_my_info ("%s", status_string);
  268   g_free(status_string);
  269 }
  270 
  271 /* called when a watched object is finalized */
  272 static void finalize_callback(gpointer data, GObject *obj)
  273 {
  274   --canvas_obj_count;
  275 }
  276 /* increase reference to object and optionally register a callback to check
  277  * for reference leaks */
  278 static void addref_canvas_obj(GObject *obj)
  279 {
  280   g_assert(obj);
  281   g_object_ref_sink(obj);
  282 
  283   if (INFO_ENABLED)
  284     {
  285       /* to check for resource leaks, we ask for a notify ... */
  286       g_object_weak_ref(obj, finalize_callback, NULL);
  287       ++canvas_obj_count;
  288     }
  289 }
  290 
  291 
  292 /* It updates controls from values of variables, and connects control
  293  * signals to callback functions */
  294 void init_diagram(GtkBuilder *xml)
  295 {
  296   GtkContainer *area;
  297   GooCanvasItem *rootitem;
  298   GooCanvasItem *item;
  299   GtkAllocation windowsize;
  300   gulong sig_id;
  301 //  GtkWidget *viewport;
  302 //  GtkStyleContext *style;
  303 //  static GdkRGBA black = {0,0,0,0};
  304 
  305   g_assert(gcanvas_ == NULL);
  306 
  307   /* get containing window and size */
  308   area = GTK_CONTAINER(gtk_builder_get_object(xml, "diagramwindow"));
  309   g_assert(area != NULL);
  310   gtk_widget_get_allocation(GTK_WIDGET(area), &windowsize);
  311 
  312   /* Creates trees */
  313   canvas_nodes = g_tree_new_full( (GCompareDataFunc)canvas_node_compare,
  314                             NULL, NULL, (GDestroyNotify)canvas_node_delete);
  315   canvas_links = g_tree_new_full( (GCompareDataFunc)canvas_link_compare,
  316                             NULL, NULL, (GDestroyNotify)canvas_link_delete);
  317 
  318   initialize_pref_controls();
  319 
  320   /* canvas */
  321   gcanvas_ = GOO_CANVAS(goo_canvas_new());
  322   g_assert(gcanvas_ != NULL);
  323 
  324   g_object_set (G_OBJECT(gcanvas_),
  325                 "background-color", "black",
  326                 NULL);
  327 
  328   goo_canvas_set_bounds(gcanvas_,
  329                         -windowsize.width/2, -windowsize.height/2,
  330                         windowsize.width/2, windowsize.height/2);
  331 
  332   gtk_widget_show(GTK_WIDGET(gcanvas_));
  333 
  334   gtk_container_add(area, GTK_WIDGET(gcanvas_));
  335 
  336   rootitem = goo_canvas_get_root_item(gcanvas_);
  337 
  338   /* Initialize background image */
  339   g_object_set(G_OBJECT(gcanvas_), "background-color", "black", NULL);
  340   init_canvas_background(rootitem);
  341 
  342 /* TODO: gtk3 handles background color via CSS... 
  343   // Make legend background color match main display background color 
  344   style = gtk_style_new();
  345   style->bg[GTK_STATE_NORMAL] = canvas_background.color;
  346   style->base[GTK_STATE_NORMAL] = canvas_background.color;
  347 
  348   // Set protocol legend background to black 
  349   viewport = GTK_WIDGET(gtk_builder_get_object(appdata.xml, "legend_viewport"));
  350   gtk_widget_set_style(viewport, style);
  351   gtk_style_set_background(style, gtk_widget_get_window(viewport), GTK_STATE_NORMAL);
  352 
  353   // should be gtk3 compatible, but doesn't work ...
  354   viewport = GTK_WIDGET(gtk_builder_get_object(appdata.xml, "legend_viewport"));
  355   gtk_widget_override_background_color(viewport,
  356                                       GTK_STATE_FLAG_NORMAL|GTK_STATE_FLAG_ACTIVE,
  357                                       &black);
  358 */
  359 
  360   item = goo_canvas_text_new(rootitem,
  361                              "",
  362                              0, 0, -1,
  363                              GOO_CANVAS_ANCHOR_NORTH_WEST,
  364                              NULL);
  365   pcap_stats_text_item = GOO_CANVAS_TEXT(item);
  366   addref_canvas_obj(G_OBJECT(pcap_stats_text_item));
  367 
  368   sig_id = g_signal_connect(G_OBJECT(area), "size-allocate",
  369                             G_CALLBACK(diagram_resize_event), gcanvas_);
  370   g_assert(sig_id > 0);
  371   sig_id = g_signal_connect(G_OBJECT(pcap_stats_text_item), "enter-notify-event",
  372                           G_CALLBACK(pcap_stats_text_item_event), NULL);
  373   g_assert(sig_id > 0);
  374   sig_id = g_signal_connect(G_OBJECT(pcap_stats_text_item), "leave-notify-event",
  375                             G_CALLBACK(pcap_stats_text_item_event), NULL);
  376   g_assert(sig_id > 0);
  377 
  378   /* Initialize the known_protocols table */
  379   delete_gui_protocols ();
  380 
  381   /* Set the already_updating global flag */
  382   already_updating = FALSE;
  383   stop_requested = FALSE;
  384 }               /* init_diagram */
  385 
  386 /*
  387  * Initialize the canvas_background structure.
  388  * Create a new group and add it to the root group.
  389  * Create a new GooCanvasPixbufItem and add it to the new group.
  390  */
  391 static void init_canvas_background(GooCanvasItem *rootitem)
  392 {
  393   canvas_background.image.item = 
  394                             goo_canvas_image_new(rootitem,
  395                                                  NULL,
  396                                                  0, 0,
  397                                                  NULL);
  398   addref_canvas_obj(G_OBJECT(canvas_background.image.item));
  399 
  400   canvas_background.use_image = pref.bck_image_enabled;
  401   canvas_background.image.path = g_strdup(pref.bck_image_path);
  402 }
  403 
  404 /*
  405  * Called by on_canvas1_size_allocate.
  406  * Try to load image from path.
  407  * On success image is scaled to the canvas size and printed.
  408  * On error a blank black image is made and printed.
  409  */
  410 static void redraw_canvas_background(GooCanvas *canvas)
  411 {
  412   GError* error = NULL;
  413   GtkAllocation canvas_size;
  414 
  415   if (canvas_background.use_image) {
  416     /* Get canvas dimensions */
  417     gtk_widget_get_allocation(GTK_WIDGET(canvas), &canvas_size);
  418 
  419     if (canvas_background.image.image) {
  420       g_object_unref(G_OBJECT(canvas_background.image.image));
  421       canvas_background.image.image = NULL;
  422     }
  423 
  424     if (canvas_background.image.path && strlen(canvas_background.image.path))
  425       canvas_background.image.image = gdk_pixbuf_new_from_file_at_scale(canvas_background.image.path,
  426                                                                         canvas_size.width, canvas_size.height,
  427                                                                         FALSE, &error);
  428 
  429     g_object_set(G_OBJECT(canvas_background.image.item),
  430                  "pixbuf", canvas_background.image.image,
  431                  "x", (double)-canvas_size.width/2,
  432                  "y", (double)-canvas_size.height/2,
  433                  "visibility", GOO_CANVAS_ITEM_VISIBLE,
  434                  NULL);
  435   } else {
  436     g_object_set(G_OBJECT(canvas_background.image.item),
  437                  "visibility", GOO_CANVAS_ITEM_HIDDEN,
  438                  NULL);
  439   }
  440 }
  441 
  442 /*
  443  * Update the background image.  Load new image if user selected another path
  444  * in preferences.  Place the background image behind the nodes and links.
  445  */
  446 static void diagram_update_background_image(GooCanvas *canvas)
  447 {
  448   /*
  449    * If the background image enable toggle or the image path has changed, we
  450    * need to update the background.
  451    */
  452   if (pref.bck_image_enabled != canvas_background.use_image
  453       || g_strcmp0(canvas_background.image.path, pref.bck_image_path)) {
  454     canvas_background.use_image = pref.bck_image_enabled;
  455     g_free(canvas_background.image.path);
  456     canvas_background.image.path = g_strdup(pref.bck_image_path);
  457     redraw_canvas_background(canvas);
  458   }
  459 
  460   /* Draw the background first */
  461   if (canvas_background.image.item) {
  462     goo_canvas_item_lower(GOO_CANVAS_ITEM(canvas_background.image.item), NULL);
  463     goo_canvas_item_request_update(GOO_CANVAS_ITEM(canvas_background.image.item));
  464   }
  465 }
  466 
  467 static gboolean diagram_resize_event(GtkWidget *widget,
  468                                      const GdkEventConfigure *event,
  469                                      GooCanvas *canvas)
  470 {
  471   GtkAllocation windowsize;
  472   g_assert(widget != NULL);
  473   g_assert(canvas == gcanvas_);
  474 
  475 
  476   gtk_widget_get_allocation(GTK_WIDGET(widget), &windowsize);
  477   goo_canvas_set_bounds(canvas,
  478                         -windowsize.width/2, -windowsize.height/2,
  479                         windowsize.width/2, windowsize.height/2);
  480 
  481   redraw_canvas_background(canvas);
  482   diagram_reposition(canvas);
  483   return FALSE;
  484 }
  485 
  486 
  487 /* delete the specified canvas node */
  488 static void canvas_node_delete(canvas_node_t *canvas_node)
  489 {
  490   if (canvas_node->node_item)
  491     {
  492       goo_canvas_item_remove(canvas_node->node_item);
  493 //      gtk_object_destroy(GTK_OBJECT(canvas_node->node_item));
  494 //      g_object_unref(G_OBJECT (canvas_node->node_item));
  495       canvas_node->node_item = NULL;
  496     }
  497   if (canvas_node->text_item)
  498     {
  499       goo_canvas_item_remove(canvas_node->text_item);
  500 //      gtk_object_destroy(GTK_OBJECT (canvas_node->text_item));
  501 //      g_object_unref(G_OBJECT (canvas_node->text_item));
  502       canvas_node->text_item = NULL;
  503     }
  504   if (canvas_node->group_item)
  505     {
  506       goo_canvas_item_remove(GOO_CANVAS_ITEM(canvas_node->group_item));
  507 //      gtk_object_destroy(GTK_OBJECT (canvas_node->group_item));
  508 //      g_object_unref(G_OBJECT (canvas_node->group_item));
  509       canvas_node->group_item = NULL;
  510     }
  511 
  512   g_free (canvas_node);
  513 }
  514 
  515 /* used to remove nodes */
  516 static void gfunc_remove_canvas_node(gpointer data, gpointer user_data)
  517 {
  518   g_tree_remove(canvas_nodes, (const node_id_t *)data);
  519 }
  520 
  521 /* used to remove links */
  522 static void gfunc_remove_canvas_link(gpointer data, gpointer user_data)
  523 {
  524   g_tree_remove(canvas_links, (const link_id_t *)data);
  525 }
  526 
  527 static void diagram_update_nodes(GooCanvas *canvas)
  528 {
  529   GList *delete_list = NULL;
  530   node_t *new_node = NULL;
  531 
  532   /* Deletes all nodes and updates traffic values */
  533   /* TODO To reduce CPU usage, I could just as well update each specific
  534    * node in update_canvas_nodes and create a new timeout function that would
  535    * make sure that old nodes get deleted by calling update_nodes, but
  536    * not as often as with diagram_refresh_period */
  537   nodes_catalog_update_all();
  538 
  539   /* Check if there are any new nodes */
  540   while ((new_node = new_nodes_pop()))
  541     check_new_node(new_node, canvas);
  542 
  543   /* Update nodes look and queue outdated canvas_nodes for deletion */
  544   g_tree_foreach(canvas_nodes,
  545            (GTraverseFunc) canvas_node_update,
  546            &delete_list);
  547 
  548   /* delete all canvas nodes queued */
  549   g_list_foreach(delete_list, gfunc_remove_canvas_node, NULL);
  550 
  551   /* free the list - list items are already destroyed */
  552   g_list_free(delete_list);
  553 
  554   /* Limit the number of nodes displayed, if a limit has been set */
  555   /* TODO check whether this is the right function to use, now that we have a more
  556    * general display_node called in update_canvas_nodes */
  557   limit_nodes ();
  558 
  559   /* Reposition canvas_nodes */
  560   if (need_reposition)
  561       diagram_reposition(canvas);
  562 }
  563 
  564 /* handle node repositioning */
  565 static void diagram_reposition(GooCanvas *canvas)
  566 {
  567   reposition_node_t rdata;
  568   init_reposition(&rdata, canvas, displayed_nodes);
  569 
  570   g_tree_foreach(canvas_nodes,
  571                  (GTraverseFunc) reposition_canvas_nodes_prep,
  572                  &rdata);
  573 
  574   rdata.center.node_i = rdata.center.n_nodes;
  575   rdata.outer.node_i = rdata.outer.n_nodes;
  576 
  577   g_tree_foreach(canvas_nodes,
  578                  (GTraverseFunc) reposition_canvas_nodes,
  579                  &rdata);
  580 
  581   clear_reposition(&rdata);
  582 
  583   need_reposition = FALSE;
  584   need_font_refresh = FALSE;
  585 }
  586 
  587 static void diagram_update_links(GooCanvas *canvas)
  588 {
  589   GList *delete_list = NULL;
  590 
  591   /* Delete old capture links and update capture link variables */
  592   links_catalog_update_all();
  593 
  594   /* Check if there are any new links */
  595   links_catalog_foreach((GTraverseFunc) check_new_link, canvas);
  596 
  597   /* Update links look
  598    * We also queue timedout links for deletion */
  599   delete_list = NULL;
  600   g_tree_foreach(canvas_links,
  601                    (GTraverseFunc) canvas_link_update,
  602                    &delete_list);
  603 
  604   /* delete all canvas links queued */
  605   g_list_foreach(delete_list, gfunc_remove_canvas_link, NULL);
  606 
  607   /* free the list - list items are already destroyed */
  608   g_list_free(delete_list);
  609 }
  610 
  611 /* Return a g_malloc()ed string of libpcap stats counters */
  612 static gchar *get_pcap_stats_string(void)
  613 {
  614   struct pcap_stat stats;
  615 
  616   if (appdata.source.type == ST_FILE)
  617     return g_strdup(_("(Capture statistics unavailable in offline mode.)"));
  618 
  619   get_capture_stats(&stats);
  620 
  621   return g_strdup_printf("%-12s %12u\n%-12s %12u\n%-12s %12u", "recv:",
  622                          stats.ps_recv, "drop:", stats.ps_drop,
  623                          "ifdrop:", stats.ps_ifdrop);
  624 }
  625 
  626 /* Update libpcap stats counters display */
  627 static void update_pcap_stats_text(GooCanvas *canvas)
  628 {
  629   gdouble xmin, xmax, ymin, ymax, xpos, ypos;
  630   GooCanvasAnchorType anchor;
  631   gchar *tmpstr;
  632 
  633   if (pref.pcap_stats_pos == STATSPOS_NONE)
  634     {
  635       g_object_set(G_OBJECT(pcap_stats_text_item),
  636                    "visibility", GOO_CANVAS_ITEM_HIDDEN,
  637                    NULL);
  638       return;
  639     }
  640 
  641   if (get_capture_status() != PLAY)
  642     return;
  643 
  644   goo_canvas_get_bounds(canvas, &xmin, &ymin, &xmax, &ymax);
  645 
  646   switch (pref.pcap_stats_pos)
  647     {
  648     case STATSPOS_UPPER_LEFT:
  649       xpos = xmin;
  650       ypos = ymin;
  651       anchor = GOO_CANVAS_ANCHOR_NORTH_WEST;
  652       break;
  653 
  654     case STATSPOS_UPPER_RIGHT:
  655       xpos = xmax;
  656       ypos = ymin;
  657       anchor = GOO_CANVAS_ANCHOR_NORTH_EAST;
  658       break;
  659 
  660     case STATSPOS_LOWER_LEFT:
  661       xpos = xmin;
  662       ypos = ymax;
  663       anchor = GOO_CANVAS_ANCHOR_SOUTH_WEST;
  664       break;
  665 
  666     case STATSPOS_LOWER_RIGHT:
  667       xpos = xmax;
  668       ypos = ymax;
  669       anchor = GOO_CANVAS_ANCHOR_SOUTH_EAST;
  670       break;
  671 
  672     default:
  673       g_error(_("Bogus statspos_t (%d) pref.pcap_stats_pos"), pref.pcap_stats_pos);
  674       return;
  675     }
  676 
  677   tmpstr = get_pcap_stats_string();
  678   g_object_set(G_OBJECT(pcap_stats_text_item),
  679                         "text", tmpstr,
  680                         "x", xpos, "y", ypos,
  681                         "font", pref.fontname,
  682                         "fill_color", pref.text_color,
  683                         "anchor", anchor,
  684                         "visibility", GOO_CANVAS_ITEM_VISIBLE,
  685                         NULL);
  686   g_free(tmpstr);
  687 }
  688 
  689 /* Refreshes the diagram. Called each refresh_period ms
  690  * 1. Checks for new protocols and displays them
  691  * 2. Updates nodes looks
  692  * 3. Updates links looks
  693  */
  694 static gboolean update_diagram(GooCanvas *canvas)
  695 {
  696   static struct timeval last_refresh_time = { 0, 0 };
  697   capstatus_t status;
  698 
  699   /* if requested and enabled, dump to xml */
  700   if (appdata.request_dump && appdata.export_file_signal)
  701     {
  702       g_warning (_("SIGUSR1 received: exporting to %s"), appdata.export_file_signal);
  703       dump_xml(appdata.export_file_signal);
  704       appdata.request_dump = FALSE;
  705     }
  706 
  707   status = get_capture_status();
  708   if (status == PAUSE)
  709     return FALSE;
  710 
  711   if (status == CAP_EOF)
  712     {
  713       gui_eof_capture ();
  714       return FALSE;
  715     }
  716 
  717   /*
  718    * It could happen that during an intensive calculation, in order
  719    * to update the GUI and make the application responsive gtk_main_iteration
  720    * is called. But that could also trigger this very function's timeout.
  721    * If we let it run twice many problems could come up. Thus,
  722    * we are preventing it with the already_updating variable
  723    */
  724 
  725   if (already_updating)
  726     {
  727       g_my_debug ("update_diagram called while already updating");
  728       return FALSE;
  729     }
  730 
  731   already_updating = TRUE;
  732   gettimeofday(&appdata.gui_now, NULL);
  733 
  734   /* update nodes */
  735   diagram_update_nodes(canvas);
  736 
  737   /* update links */
  738   diagram_update_links(canvas);
  739 
  740   /* update background image */
  741   diagram_update_background_image(canvas);
  742 
  743   /* Update protocol information */
  744   protocol_summary_update_all();
  745 
  746   /* update proto legend */
  747   update_legend();
  748 
  749   /* Now update info windows */
  750   update_info_windows(NULL);
  751 
  752   update_pcap_stats_text(canvas);
  753 
  754   /* With this we make sure that we don't overload the
  755    * CPU with redraws */
  756 
  757   if ((last_refresh_time.tv_sec == 0) && (last_refresh_time.tv_usec == 0))
  758     last_refresh_time = appdata.gui_now;
  759 
  760   /* Force redraw */
  761   while (gtk_events_pending ())
  762     gtk_main_iteration ();
  763 
  764   gettimeofday(&appdata.gui_now, NULL);
  765   last_refresh_time = appdata.gui_now;
  766 
  767   already_updating = FALSE;
  768 
  769   if (stop_requested)
  770     gui_stop_capture();
  771 
  772   return TRUE;          /* Keep on calling this function */
  773 }               /* update_diagram */
  774 
  775 static void purge_expired_legend_protocol(GtkWidget *widget, gpointer data)
  776 {
  777   GtkLabel *lab = GTK_LABEL(widget);
  778   if (lab &&
  779       !protocol_summary_find(pref.stack_level, gtk_label_get_label(lab)))
  780     {
  781       /* protocol expired, remove */
  782       gtk_widget_destroy(widget);
  783       known_protocols--;
  784     }
  785 }
  786 
  787 /* updates the legend */
  788 static void update_legend()
  789 {
  790   GtkWidget *prot_table;
  791 
  792   /* first, check if there are expired protocols */
  793   prot_table = GTK_WIDGET(gtk_builder_get_object(appdata.xml, "prot_table"));
  794   if (!prot_table)
  795     return;
  796 
  797   gtk_container_foreach(GTK_CONTAINER(prot_table),
  798                         (GtkCallback)purge_expired_legend_protocol, NULL);
  799 
  800   /* then search for new protocols */
  801   check_new_protocol(prot_table, protocol_summary_stack());
  802 }
  803 
  804 
  805 /* Checks whether there is already a legend entry for each known
  806  * protocol. If not, create it */
  807 static void check_new_protocol (GtkWidget *prot_table, const protostack_t *pstk)
  808 {
  809   const GList *protocol_item;
  810   const protocol_t *protocol;
  811   const GdkRGBA *color;
  812 //  GtkStyle *style;
  813   GtkLabel *lab;
  814   GtkWidget *newlab;
  815   GList *childlist;
  816   gchar *mkupbuf = NULL;
  817 
  818   if (!pstk)
  819     return; /* nothing to do */
  820 
  821   childlist = gtk_container_get_children(GTK_CONTAINER(prot_table));
  822   protocol_item = pstk->protostack[pref.stack_level];
  823   while (protocol_item)
  824     {
  825       const GList *cur;
  826       protocol = protocol_item->data;
  827 
  828       /* prepare next */
  829       protocol_item = protocol_item->next;
  830 
  831       /* First, we check whether the diagram already knows about this protocol,
  832        * checking whether it is shown on the legend. */
  833       cur = childlist;
  834       while (cur)
  835         {
  836           lab = GTK_LABEL(cur->data);
  837           if (lab && !strcmp(protocol->name, gtk_label_get_label(lab)))
  838             break; /* found */
  839           cur = cur->next;
  840         }
  841 
  842       if (cur)
  843           continue; /* found, skip to next */
  844 
  845       g_my_debug ("Protocol '%s' not found. Creating legend item",
  846                   protocol->name);
  847 
  848       /* It's not, so we build a new entry on the legend */
  849 
  850       
  851       /* we add the new label widgets */
  852       newlab = gtk_label_new(NULL);
  853       gtk_widget_show (newlab);
  854 //      gtk_misc_set_alignment(GTK_MISC(newlab), 0, 0);
  855 
  856       color = protohash_color(protocol->name);
  857       mkupbuf = g_markup_printf_escaped(
  858                         "<span foreground=\"#%02x%02x%02x\">%s</span>",
  859                         (unsigned int)(color->red * 255),
  860                         (unsigned int)(color->green * 255),
  861                         (unsigned int)(color->blue * 255),
  862                         protocol->name);    
  863       gtk_label_set_markup(GTK_LABEL(newlab), mkupbuf);
  864       g_free(mkupbuf);
  865       
  866 /*      if (!gdk_colormap_alloc_color
  867           (gdk_colormap_get_system(), &color, FALSE, TRUE))
  868         g_warning (_("Unable to allocate color for new protocol %s"),
  869                    protocol->name);
  870       style = gtk_style_new ();
  871       style->fg[GTK_STATE_NORMAL] = color;
  872       gtk_widget_set_style (newlab, style);
  873       g_object_unref (style);
  874 */
  875      
  876       gtk_container_add(GTK_CONTAINER(prot_table), newlab);
  877       known_protocols++;
  878     }
  879   g_list_free(childlist);
  880 }               /* check_new_protocol */
  881 
  882 /* empties the table of protocols */
  883 void delete_gui_protocols(void)
  884 {
  885   GList *item;
  886   GtkContainer *prot_table;
  887 
  888   known_protocols = 0;
  889 
  890   /* restart color cycle */
  891   protohash_reset_cycle();
  892 
  893   /* remove proto labels from legend */
  894   prot_table = GTK_CONTAINER(gtk_builder_get_object(appdata.xml, "prot_table"));
  895   item = gtk_container_get_children (GTK_CONTAINER (prot_table));
  896   while (item)
  897     {
  898       gtk_container_remove (prot_table, item->data);
  899       item = item->next;
  900     }
  901 
  902   gtk_widget_queue_resize (GTK_WIDGET (appdata.app1));
  903 }               /* delete_gui_protocols */
  904 
  905 /* Checks if there is a canvas_node per each node. If not, one canvas_node
  906  * must be created and initiated */
  907 static gint check_new_node(node_t * node, GooCanvas *canvas)
  908 {
  909   canvas_node_t *new_canvas_node;
  910   gulong sig_id; 
  911 
  912   if (!node)
  913     return FALSE;
  914 
  915   if (display_node(node) && !g_tree_lookup(canvas_nodes, &node->node_id))
  916     {
  917       GooCanvasItem *rootgroup;
  918       GooCanvasItem *newgroup;
  919       
  920       new_canvas_node = g_malloc (sizeof (canvas_node_t));
  921       g_assert(new_canvas_node);
  922 
  923       new_canvas_node->canvas_node_id = node->node_id;
  924 
  925       /* Create a new group to hold the node and its labels */
  926       rootgroup = goo_canvas_get_root_item(canvas);
  927       newgroup = goo_canvas_group_new(rootgroup,
  928                                       "x", 100.0,
  929                                       "y", 100.0, 
  930                                       NULL);
  931       addref_canvas_obj(G_OBJECT(newgroup));
  932       new_canvas_node->group_item = GOO_CANVAS_GROUP(newgroup);
  933 
  934       /* create circle and text, initially hidden until proper repositioned */
  935       new_canvas_node->node_item = goo_canvas_ellipse_new(newgroup,
  936                                  0.0,
  937                                  0.0,
  938                                  0.0,
  939                                  0.0,
  940                                  "fill-color", "white",
  941                                  "stroke-color", "black",
  942                                  "line-width", 0,
  943                                  "visibility", GOO_CANVAS_ITEM_INVISIBLE,
  944                                  NULL);
  945       addref_canvas_obj(G_OBJECT(new_canvas_node->node_item));
  946 
  947       new_canvas_node->text_item = goo_canvas_text_new(newgroup,
  948                    node->name->str,
  949                    0.0,
  950                    0.0,
  951                    -1,
  952                    GOO_CANVAS_ANCHOR_CENTER,
  953                    "font", pref.fontname,
  954                    "fill-color", pref.text_color,
  955                    "visibility", GOO_CANVAS_ITEM_INVISIBLE,
  956                     NULL);
  957       addref_canvas_obj(G_OBJECT(new_canvas_node->text_item));
  958 
  959       goo_canvas_item_raise(new_canvas_node->text_item, NULL);
  960       sig_id = g_signal_connect(G_OBJECT(new_canvas_node->text_item),
  961                                 "button-release-event",
  962                                 G_CALLBACK(node_item_event),
  963                                 new_canvas_node);
  964       g_assert(sig_id > 0);
  965       sig_id = g_signal_connect(G_OBJECT(new_canvas_node->node_item),
  966                                 "button-release-event",
  967                                 G_CALLBACK(node_item_event), 
  968                                 new_canvas_node);
  969       g_assert(sig_id > 0);
  970 
  971       if (!new_canvas_node->node_item || !new_canvas_node->text_item)
  972         g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, _("Canvas node null"));
  973 
  974       new_canvas_node->is_new = TRUE;
  975       new_canvas_node->shown = TRUE;
  976       new_canvas_node->centered = FALSE;
  977 
  978       g_tree_insert (canvas_nodes,
  979              &new_canvas_node->canvas_node_id, new_canvas_node);
  980       g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
  981          _("Creating canvas_node: %s. Number of nodes %d"),
  982          node->name->str, g_tree_nnodes (canvas_nodes));
  983 
  984       need_reposition = TRUE;
  985     }
  986 
  987   return FALSE;         /* False to keep on traversing */
  988 }               /* check_new_node */
  989 
  990 
  991 /* - updates sizes, names, etc */
  992 static gint canvas_node_update(node_id_t *node_id, canvas_node_t *canvas_node,
  993                                GList **delete_list)
  994 {
  995   node_t *node;
  996   gdouble node_size;
  997   static clock_t start = 0;
  998   clock_t end;
  999   gdouble cpu_time_used;
 1000   char *nametmp = NULL;
 1001 
 1002   node = nodes_catalog_find(node_id);
 1003 
 1004   /* Remove node if node is too old or if capture is stopped */
 1005   if (!node || !display_node (node))
 1006     {
 1007       /* adds current to list of canvas nodes to delete */
 1008       *delete_list = g_list_prepend( *delete_list, node_id);
 1009       g_my_debug ("Queueing canvas node to remove.");
 1010       need_reposition = TRUE;
 1011       return FALSE;
 1012     }
 1013 
 1014   switch (pref.node_size_variable)
 1015     {
 1016     case INST_TOTAL:
 1017       node_size = get_node_size(node->node_stats.stats.average);
 1018       break;
 1019     case INST_INBOUND:
 1020       node_size = get_node_size(node->node_stats.stats_in.average);
 1021       break;
 1022     case INST_OUTBOUND:
 1023       node_size = get_node_size(node->node_stats.stats_out.average);
 1024       break;
 1025     case INST_PACKETS:
 1026       node_size = get_node_size(node->node_stats.pkt_list.length);
 1027       break;
 1028     case ACCU_TOTAL:
 1029       node_size = get_node_size(node->node_stats.stats.accumulated);
 1030       break;
 1031     case ACCU_INBOUND:
 1032       node_size = get_node_size(node->node_stats.stats_in.accumulated);
 1033       break;
 1034     case ACCU_OUTBOUND:
 1035       node_size = get_node_size(node->node_stats.stats_out.accumulated);
 1036       break;
 1037     case ACCU_PACKETS:
 1038       node_size = get_node_size(node->node_stats.stats.accu_packets);
 1039       break;
 1040     case ACCU_AVG_SIZE:
 1041       node_size = get_node_size(node->node_stats.stats.avg_size);
 1042       break;
 1043     default:
 1044       node_size = get_node_size(node->node_stats.stats_out.average);
 1045       g_warning (_("Unknown value or node_size_variable"));
 1046     }
 1047 
 1048   /* limit the maximum size to avoid overload */
 1049   if (node_size > MAX_NODE_SIZE)
 1050     node_size = MAX_NODE_SIZE;
 1051 
 1052   if (node->main_prot[pref.stack_level])
 1053     {
 1054       canvas_node->color = *protohash_color(node->main_prot[pref.stack_level]);
 1055 
 1056       g_object_set(G_OBJECT(canvas_node->node_item),
 1057                    "radius-x", node_size / 2,
 1058                    "radius-y", node_size / 2,
 1059                    "fill-color-gdk-rgba", &canvas_node->color,
 1060                    "visibility", GOO_CANVAS_ITEM_VISIBLE,
 1061                    NULL);
 1062       goo_canvas_item_show(canvas_node->text_item);
 1063     }
 1064   else
 1065     {
 1066       guint32 black = 0x000000ff;
 1067       g_object_set(G_OBJECT(canvas_node->node_item),
 1068                    "radius-x", node_size / 2,
 1069                    "radius-y", node_size / 2,
 1070                    "fill_color_rgba", black, 
 1071                    "visibility", GOO_CANVAS_ITEM_VISIBLE,
 1072                    NULL);
 1073     }
 1074 
 1075   /* We check the name of the node, and update the canvas node name
 1076    * if it has changed (useful for non blocking dns resolving) */
 1077   if (canvas_node->text_item)
 1078     {
 1079       g_object_get(G_OBJECT(canvas_node->text_item),
 1080                    "text", &nametmp,
 1081                    NULL);
 1082       if (strcmp(nametmp, node->name->str))
 1083         {
 1084           g_object_set(G_OBJECT(canvas_node->text_item),
 1085                        "text", node->name->str,
 1086                        NULL);
 1087           goo_canvas_item_request_update(canvas_node->text_item);
 1088         }
 1089       g_free (nametmp);
 1090     }
 1091 
 1092   /* Processor time check. If too much time has passed, update the GUI */
 1093   end = clock ();
 1094   cpu_time_used = ((gdouble) (end - start)) / CLOCKS_PER_SEC;
 1095   if (cpu_time_used > 0.05)
 1096     {
 1097       /* Force redraw */
 1098       while (gtk_events_pending ())
 1099         gtk_main_iteration ();
 1100       start = end;
 1101     }
 1102   return FALSE;         /* False means keep on calling the function */
 1103 
 1104 }               /* update_canvas_nodes */
 1105 
 1106 
 1107 /* Returns whether the node in question should be displayed in the
 1108  * diagram or not */
 1109 static gboolean display_node(node_t *node)
 1110 {
 1111   double diffms;
 1112 
 1113   if (!node)
 1114     return FALSE;
 1115 
 1116   diffms = subtract_times_ms(&appdata.now, &node->node_stats.stats.last_time);
 1117 
 1118   /* There are problems if a canvas_node is deleted if it still
 1119    * has packets, so we have to check that as well */
 1120 
 1121   /* Remove canvas_node if node is too old */
 1122   if (diffms >= pref.gui_node_timeout_time
 1123       && pref.gui_node_timeout_time
 1124       && !node->node_stats.pkt_list.length)
 1125     return FALSE;
 1126 
 1127 #if 1
 1128   if ((pref.gui_node_timeout_time == 1) && !node->node_stats.pkt_list.length)
 1129     g_my_critical ("Impossible situation in display node");
 1130 #endif
 1131 
 1132   return TRUE;
 1133 }               /* display_node */
 1134 
 1135 /* Sorts canvas nodes with the criterium set in preferences and sets
 1136  * which will be displayed in the diagram */
 1137 static void limit_nodes(void)
 1138 {
 1139   static GTree *ordered_nodes = NULL;
 1140   static guint limit;
 1141   displayed_nodes = 0;      /* We'll increment for each node we don't
 1142                  * limit */
 1143 
 1144   if (appdata.node_limit < 0)
 1145     {
 1146       displayed_nodes = g_tree_nnodes (canvas_nodes);
 1147       return;
 1148     }
 1149 
 1150   limit = appdata.node_limit;
 1151 
 1152   ordered_nodes = g_tree_new (traffic_compare);
 1153 
 1154   g_tree_foreach(canvas_nodes, (GTraverseFunc) add_ordered_node, ordered_nodes);
 1155   g_tree_foreach(ordered_nodes, (GTraverseFunc) check_ordered_node,
 1156            &limit);
 1157   g_tree_destroy (ordered_nodes);
 1158   ordered_nodes = NULL;
 1159 }               /* limit_nodes */
 1160 
 1161 static gint add_ordered_node(node_id_t *node_id, canvas_node_t *node,
 1162                              GTree * ordered_nodes)
 1163 {
 1164   g_tree_insert(ordered_nodes, node, node);
 1165   g_my_debug("Adding ordered node. Number of nodes: %d",
 1166              g_tree_nnodes(ordered_nodes));
 1167   return FALSE;         /* keep on traversing */
 1168 }               /* add_ordered_node */
 1169 
 1170 static gint check_ordered_node(gdouble *traffic, canvas_node_t *node, guint *count)
 1171 {
 1172   /* TODO We can probably optimize this by stopping the traversion once
 1173    * the limit has been reached */
 1174   if (*count) {
 1175     if (!node->shown)
 1176       need_reposition = TRUE;
 1177     node->shown = TRUE;
 1178     ++displayed_nodes;
 1179     (*count)--;
 1180   } else {
 1181     if (node->shown)
 1182       need_reposition = TRUE;
 1183     node->shown = FALSE;
 1184   }
 1185   return FALSE;            /* keep on traversing */
 1186 }
 1187 
 1188 /* Comparison function used to order the (GTree *) nodes
 1189  * and canvas_nodes heard on the network */
 1190 static gint traffic_compare(gconstpointer a, gconstpointer b)
 1191 {
 1192   node_t *node_a, *node_b;
 1193 
 1194   g_assert (a != NULL);
 1195   g_assert (b != NULL);
 1196 
 1197   node_a = (node_t *) a;
 1198   node_b = (node_t *) b;
 1199 
 1200   if (node_a->node_stats.stats.average < node_b->node_stats.stats.average)
 1201     return 1;
 1202   if (node_a->node_stats.stats.average > node_b->node_stats.stats.average)
 1203     return -1;
 1204 
 1205   /* If two nodes have the same traffic, we still have
 1206    * to distinguish them somehow. We use the node_id */
 1207 
 1208   return (node_id_compare (&node_a->node_id, &node_b->node_id));
 1209 
 1210 }               /* traffic_compare */
 1211 
 1212 /* initialize reposition struct */
 1213 static void init_reposition(reposition_node_t *data,
 1214                             GooCanvas *canvas,
 1215                             guint total_nodes)
 1216 {
 1217   gdouble text_compensation = 50;
 1218 
 1219   data->canvas = canvas;
 1220   memset(&data->center, 0, sizeof(data->center));
 1221   memset(&data->outer, 0, sizeof(data->outer));
 1222 
 1223   /*
 1224    * Offset the starting angle on the center ring so that when there are
 1225    * relatively few nodes displayed (e.g. 4 central and 4 outer) the links
 1226    * obscure things less (by not overlapping node labels and other links).
 1227    */
 1228   data->center.angle += M_PI / 4.0;
 1229 
 1230   if (appdata.column_patterns)
 1231     data->column_populations = g_malloc0(appdata.column_patterns->len + 1 *
 1232                                            sizeof(*data->column_populations));
 1233 
 1234   goo_canvas_get_bounds(canvas,
 1235                         &data->xmin, &data->ymin,
 1236                         &data->xmax, &data->ymax);
 1237 
 1238 
 1239   data->xmin += text_compensation;
 1240   data->xmax -= text_compensation;  /* Reduce the drawable area so that
 1241                  * the node name is not lost
 1242                  * TODO: Need a function to calculate
 1243                  * text_compensation depending on font size */
 1244   data->x_rad_max = 0.9 * (data->xmax - data->xmin) / 2;
 1245   data->y_rad_max = 0.9 * (data->ymax - data->ymin) / 2;
 1246   data->x_inner_rad_max = data->x_rad_max / 2;
 1247   data->y_inner_rad_max = data->y_rad_max / 2;
 1248 }
 1249 
 1250 static void clear_reposition(reposition_node_t *rdata)
 1251 {
 1252   if (appdata.column_patterns)
 1253     g_free(rdata->column_populations);
 1254 }
 1255 
 1256 static guint find_node_column(node_t *node)
 1257 {
 1258   guint i;
 1259 
 1260   /* This should only be called if we're in columnar-positioning mode */
 1261   g_assert(appdata.column_patterns);
 1262 
 1263   for (i = 0; i < appdata.column_patterns->len; i++)
 1264     {
 1265       if (node_matches_spec_list(node, g_ptr_array_index(appdata.column_patterns, i)))
 1266         return i;
 1267     }
 1268 
 1269   /*
 1270    * If no explicit match was found it goes in the rightmost column (with an
 1271    * implicit "match-all" pattern).
 1272    */
 1273   return appdata.column_patterns->len;
 1274 }
 1275 
 1276 /*
 1277  * A preparatory pass to count how many nodes are centered and how many are on
 1278  * the outer ring (and mark each appropriately).  Also does analogous work for
 1279  * columnar-positioning mode (count nodes in each column and mark each node with
 1280  * its column).
 1281  */
 1282 static gint reposition_canvas_nodes_prep(const node_id_t *node_id,
 1283                                          canvas_node_t *canvas_node,
 1284                                          reposition_node_t *rdata)
 1285 {
 1286   node_t *node;
 1287 
 1288   if (!canvas_node->shown)
 1289     return FALSE;
 1290 
 1291   node = nodes_catalog_find(node_id);
 1292   if (appdata.column_patterns)
 1293     {
 1294       canvas_node->column = find_node_column(node);
 1295       canvas_node->column_idx = rdata->column_populations[canvas_node->column]++;
 1296     }
 1297   else if (node && node_matches_spec_list(node, centered_node_speclist))
 1298     {
 1299       canvas_node->centered = TRUE;
 1300       rdata->center.n_nodes++;
 1301     }
 1302   else
 1303     {
 1304       canvas_node->centered = FALSE;
 1305       rdata->outer.n_nodes++;
 1306     }
 1307 
 1308   return FALSE;
 1309 }
 1310 
 1311 /*
 1312  * Return a point between 'min' and 'max' appropriate for position number 'pos'
 1313  * out of 'num' total possible positions (basically pos/num of the way between
 1314  * min and max, though with some tweaking to keep things away from the very
 1315  * edges of the range).
 1316  */
 1317 static gdouble scale_within(gdouble min, gdouble max, guint pos, guint num)
 1318 {
 1319   return min + (((max - min) / (num + 1)) * (pos + 1));
 1320 }
 1321 
 1322 /* Called from update_diagram if the global need_reposition
 1323  * is set. It rearranges the nodes*/
 1324 static gint reposition_canvas_nodes(node_id_t * node_id,
 1325                                     canvas_node_t * canvas_node,
 1326                                     reposition_node_t *data)
 1327 {
 1328   struct node_ring *ring;
 1329   gdouble center_x, center_y, oddAngle;
 1330   gdouble x = 0, y = 0;
 1331 
 1332   if (!canvas_node->shown)
 1333     {
 1334       goo_canvas_item_hide(canvas_node->node_item);
 1335       goo_canvas_item_hide(canvas_node->text_item);
 1336       return FALSE;
 1337     }
 1338 
 1339   ring = canvas_node->centered ? &data->center : &data->outer;
 1340 
 1341   center_x = (data->xmax - data->xmin) / 2 + data->xmin;
 1342   center_y = (data->ymax - data->ymin) / 2 + data->ymin;
 1343 
 1344   /* TODO I've done all the stationary changes in a hurry
 1345    * I should review it an tidy up all this stuff */
 1346   if (appdata.stationary_layout)
 1347     {
 1348       if (canvas_node->is_new)
 1349     {
 1350       static guint count = 0, base = 1;
 1351       gdouble s_angle = 0;
 1352 
 1353       if (count == 0)
 1354         {
 1355           s_angle = M_PI * 2.0f;
 1356           count++;
 1357         }
 1358       else
 1359         {
 1360 
 1361           if (count > 2 * base)
 1362         {
 1363           base *= 2;
 1364           count = 1;
 1365         }
 1366           s_angle = M_PI * (gdouble) count / ((gdouble) base);
 1367           count += 2;
 1368         }
 1369       x = data->x_rad_max * cos (s_angle);
 1370       y = data->y_rad_max * sin (s_angle);
 1371     }
 1372     }
 1373   else if (appdata.column_patterns)
 1374     {
 1375       guint col = canvas_node->column;
 1376 
 1377       x = scale_within(data->xmin, data->xmax, canvas_node->column,
 1378                        appdata.column_patterns->len + 1);
 1379       y = scale_within(data->ymin, data->ymax, canvas_node->column_idx,
 1380                        data->column_populations[col]);
 1381     }
 1382   else
 1383     {
 1384       if (canvas_node->centered && data->center.n_nodes == 1)
 1385         {
 1386           /* one centered node, reset coordinates */
 1387           x = center_x;
 1388           y = center_y;
 1389           ring->angle -= 2 * M_PI / ring->n_nodes;
 1390         }
 1391       else
 1392         {
 1393           if (ring->n_nodes % 2 == 0)   /* spacing is better when n_nodes is odd and Y is linear */
 1394             oddAngle = (ring->angle * ring->n_nodes) / (ring->n_nodes + 1);
 1395           else
 1396             oddAngle = ring->angle;
 1397 
 1398           if (ring->n_nodes > 7)
 1399             {
 1400               x = data->x_rad_max * cos (oddAngle);
 1401               y = data->y_rad_max * asin (sin (oddAngle)) / (M_PI / 2);
 1402             }
 1403           else
 1404             {
 1405               x = data->x_rad_max * cos (ring->angle);
 1406               y = data->y_rad_max * sin (ring->angle);
 1407             }
 1408 
 1409           if (canvas_node->centered && data->center.n_nodes > 1)
 1410             {
 1411               /* For the inner ring, just move it proportionally closer the the center point. */
 1412               x = center_x + ((x - center_x) * pref.inner_ring_scale);
 1413               y = center_y + ((y - center_y) * pref.inner_ring_scale);
 1414             }
 1415         }
 1416     }
 1417 
 1418 
 1419   if (!appdata.stationary_layout || canvas_node->is_new)
 1420     {
 1421       g_object_set( G_OBJECT(canvas_node->group_item),
 1422                     "x", x,
 1423                     "y", y,
 1424                     NULL);
 1425       canvas_node->is_new = FALSE;
 1426     }
 1427 
 1428   if (need_font_refresh)
 1429     {
 1430       /* update text font */
 1431       g_object_set( G_OBJECT(canvas_node->text_item),
 1432                     "font", pref.fontname,
 1433                     "fill_color", pref.text_color,
 1434                     NULL);
 1435     }
 1436 
 1437   if (pref.diagram_only)
 1438     {
 1439       goo_canvas_item_hide(canvas_node->text_item);
 1440     }
 1441   else
 1442     {
 1443       goo_canvas_item_show(canvas_node->text_item);
 1444       goo_canvas_item_request_update(canvas_node->text_item);
 1445     }
 1446 
 1447   goo_canvas_item_show(canvas_node->node_item);
 1448   goo_canvas_item_request_update(canvas_node->node_item);
 1449 
 1450   ring->node_i--;
 1451 
 1452   if (ring->node_i)
 1453     ring->angle += 2 * M_PI / ring->n_nodes;
 1454   else
 1455     {
 1456       ring->angle = 0.0;
 1457       ring->n_nodes = 0;
 1458     }
 1459 
 1460   return FALSE;
 1461 }
 1462 
 1463 
 1464 /* Goes through all known links and checks whether there already exists
 1465  * a corresponding canvas_link. If not, create it.*/
 1466 static gint check_new_link(link_id_t * link_id, link_t * link, GooCanvas *canvas)
 1467 {
 1468   canvas_link_t *new_canvas_link;
 1469   gulong sig_id;
 1470   GooCanvasItem *rootgroup;
 1471 
 1472   if (!g_tree_lookup (canvas_links, link_id))
 1473     {
 1474       rootgroup = goo_canvas_get_root_item(canvas);
 1475 
 1476       new_canvas_link = g_malloc (sizeof (canvas_link_t));
 1477       g_assert(new_canvas_link);
 1478       new_canvas_link->canvas_link_id = *link_id;
 1479 
 1480       /* set the lines position using groups positions */
 1481       new_canvas_link->src_item =
 1482             goo_canvas_polyline_new(rootgroup, TRUE, 2,
 1483                                     0,0,
 1484                                     1,1,
 1485                                     "fill-color", "tan",
 1486                                     NULL);
 1487       g_object_ref(G_OBJECT (new_canvas_link->src_item));
 1488 
 1489       new_canvas_link->dst_item =
 1490             goo_canvas_polyline_new(rootgroup, TRUE, 2,
 1491                                     0,0,
 1492                                     1,1,
 1493                                     "fill-color", "tan",
 1494                                     NULL);
 1495       g_object_ref(G_OBJECT (new_canvas_link->dst_item));
 1496 
 1497 
 1498       g_tree_insert (canvas_links,
 1499              &new_canvas_link->canvas_link_id, new_canvas_link);
 1500       goo_canvas_item_lower(new_canvas_link->src_item, NULL);
 1501       goo_canvas_item_lower(new_canvas_link->dst_item, NULL);
 1502 
 1503       sig_id = g_signal_connect(G_OBJECT(new_canvas_link->src_item), 
 1504                                 "button-release-event",
 1505                                 G_CALLBACK(link_item_event),
 1506                                 new_canvas_link);
 1507       g_assert(sig_id > 0);
 1508       sig_id = g_signal_connect(G_OBJECT(new_canvas_link->dst_item), 
 1509                                 "button-release-event",
 1510                                 G_CALLBACK(link_item_event),
 1511                                 new_canvas_link);
 1512 
 1513       sig_id = g_signal_connect(G_OBJECT(new_canvas_link->src_item),
 1514                                 "enter-notify-event",
 1515                                 G_CALLBACK(link_item_event),
 1516                                 new_canvas_link);
 1517       g_assert(sig_id > 0);
 1518       sig_id = g_signal_connect(G_OBJECT(new_canvas_link->src_item),
 1519                                 "leave-notify-event",
 1520                                 G_CALLBACK(link_item_event),
 1521                                 new_canvas_link);
 1522       g_assert(sig_id > 0);
 1523     }
 1524 
 1525   return FALSE;
 1526 }               /* check_new_link */
 1527 
 1528 
 1529 /* - calls update_links, so that the related link updates its average
 1530  *   traffic and main protocol, and old links are deleted
 1531  * - caculates link size and color fading */
 1532 static gint canvas_link_update(link_id_t * link_id, canvas_link_t * canvas_link,
 1533                                GList **delete_list)
 1534 {
 1535   const link_t *link;
 1536   const canvas_node_t *canvas_dst;
 1537   const canvas_node_t *canvas_src;
 1538   GdkRGBA scaledColor;
 1539   double xs, ys, xd, yd, scale;
 1540 
 1541   /* We used to run update_link here, but that was a major performance penalty,
 1542    * and now it is done in update_diagram */
 1543   link = links_catalog_find(link_id);
 1544   if (!link)
 1545     {
 1546       *delete_list = g_list_prepend( *delete_list, link_id);
 1547       g_my_debug ("Queueing canvas link to remove.");
 1548       return FALSE;
 1549     }
 1550 
 1551   /* If either source or destination has disappeared, we hide the link
 1552        * until it can be show again */
 1553 
 1554   /* We get coords for the destination node */
 1555   canvas_dst = g_tree_lookup (canvas_nodes, &link_id->dst);
 1556   if (!canvas_dst || !canvas_dst->shown)
 1557     {
 1558       goo_canvas_item_hide(canvas_link->src_item);
 1559       goo_canvas_item_hide(canvas_link->dst_item);
 1560       return FALSE;
 1561     }
 1562 
 1563   /* We get coords from source node */
 1564   canvas_src = g_tree_lookup (canvas_nodes, &link_id->src);
 1565   if (!canvas_src || !canvas_src->shown)
 1566     {
 1567       goo_canvas_item_hide(canvas_link->src_item);
 1568       goo_canvas_item_hide(canvas_link->dst_item);
 1569       return FALSE;
 1570     }
 1571 
 1572   /* What if there never is a protocol?
 1573    * I have to initialize canvas_link->color to a known value */
 1574   if (link->main_prot[pref.stack_level])
 1575     {
 1576       double diffms;
 1577       canvas_link->color = *protohash_color(link->main_prot[pref.stack_level]);
 1578 
 1579       /* scale color down to 10% at link timeout */
 1580       diffms = subtract_times_ms(&appdata.now, &link->link_stats.stats.last_time);
 1581       scale = pow(0.10, diffms / pref.gui_link_timeout_time);
 1582 
 1583       scaledColor.red = scale * canvas_link->color.red;
 1584       scaledColor.green = scale * canvas_link->color.green;
 1585       scaledColor.blue = scale * canvas_link->color.blue;
 1586       scaledColor.alpha = 1;
 1587     }
 1588   else
 1589     {
 1590       // black
 1591       scaledColor.red = 0;
 1592       scaledColor.green = 0;
 1593       scaledColor.blue = 0;
 1594       scaledColor.alpha = 1;
 1595     }
 1596 
 1597   /* retrieve coordinates of node centers */
 1598   g_object_get(G_OBJECT(canvas_src->group_item), "x", &xs, "y", &ys, NULL);
 1599   g_object_get(G_OBJECT(canvas_dst->group_item), "x", &xd, "y", &yd, NULL);
 1600 
 1601   /* first draw triangle for src->dst */
 1602   draw_oneside_link(xs, ys, xd, yd, &(link->link_stats.stats_out), &scaledColor,
 1603                     canvas_link->src_item);
 1604 
 1605   /* then draw triangle for dst->src */
 1606   draw_oneside_link(xd, yd, xs, ys, &(link->link_stats.stats_in), &scaledColor,
 1607                     canvas_link->dst_item);
 1608 
 1609   return FALSE;
 1610 
 1611 }               /* update_canvas_links */
 1612 
 1613 /* given the src and dst node centers, plus a size, draws a triangle in the
 1614  * specified color on the provided canvas item*/
 1615 static void draw_oneside_link(double xs, double ys, double xd, double yd,
 1616                               const basic_stats_t *link_stats,
 1617                               const GdkRGBA *scaledColor, GooCanvasItem *item)
 1618 {
 1619   GooCanvasPoints *points;
 1620   gdouble versorx, versory, modulus, link_size;
 1621 
 1622   link_size = get_link_size(link_stats) / 2;
 1623 
 1624   /* limit the maximum size to avoid overload */
 1625   if (link_size > MAX_LINK_SIZE)
 1626     link_size = MAX_LINK_SIZE;
 1627 
 1628   versorx = -(yd - ys);
 1629   versory = xd - xs;
 1630   modulus = sqrt (pow (versorx, 2) + pow (versory, 2));
 1631   if (modulus == 0)
 1632     {
 1633       link_size = 0;
 1634       modulus = 1;
 1635     }
 1636 
 1637   points = goo_canvas_points_new(3);
 1638   points->coords[0] = xd;
 1639   points->coords[1] = yd;
 1640   points->coords[2] = xs + versorx * link_size / modulus;
 1641   points->coords[3] = ys + versory * link_size / modulus;
 1642   points->coords[4] = xs - versorx * link_size / modulus;
 1643   points->coords[5] = ys - versory * link_size / modulus;
 1644 
 1645   /* If we got this far, the link can be shown. Make sure it is */
 1646   g_object_set(G_OBJECT(item),
 1647                "points", points,
 1648                "fill-color-gdk-rgba", scaledColor,
 1649                "stroke-color-gdk-rgba", scaledColor,
 1650                "visibility", GOO_CANVAS_ITEM_VISIBLE,
 1651                NULL);
 1652 
 1653   goo_canvas_points_unref(points);
 1654 }
 1655 
 1656 
 1657 
 1658 /* Returs the radius in pixels given average traffic and size mode */
 1659 static gdouble get_node_size (gdouble average)
 1660 {
 1661   gdouble result = 0.0;
 1662   switch (pref.size_mode)
 1663     {
 1664     case LINEAR:
 1665       result = average + 1;
 1666       break;
 1667     case LOG:
 1668       result = log (average + 1);
 1669       break;
 1670     case SQRT:
 1671       result = sqrt (average + 1);
 1672       break;
 1673     }
 1674   return 5.0 + pref.node_radius_multiplier * result;
 1675 }
 1676 
 1677 /* Returs the width in pixels given average traffic and size mode */
 1678 static gdouble get_link_size (const basic_stats_t *link_stats)
 1679 {
 1680   gdouble result = 0.0;
 1681   gdouble data;
 1682 
 1683   /* since links are one-sided, there's no distinction between inbound/outbound
 1684      data.   */
 1685   switch (pref.node_size_variable)
 1686     {
 1687     case INST_TOTAL:
 1688     case INST_INBOUND:
 1689     case INST_OUTBOUND:
 1690     case INST_PACKETS: /* active packets stat not available */
 1691       data = link_stats->average;
 1692       break;
 1693     case ACCU_TOTAL:
 1694     case ACCU_INBOUND:
 1695     case ACCU_OUTBOUND:
 1696       data = link_stats->accumulated;
 1697       break;
 1698     case ACCU_PACKETS:
 1699       data = link_stats->accu_packets;
 1700       break;
 1701     case ACCU_AVG_SIZE:
 1702       data = link_stats->avg_size;
 1703       break;
 1704     default:
 1705       data = link_stats->average;
 1706       g_warning (_("Unknown value for link_size_variable"));
 1707     }
 1708   switch (pref.size_mode)
 1709     {
 1710     case LINEAR:
 1711       result = data + 1;
 1712       break;
 1713     case LOG:
 1714       result = log(data + 1);
 1715       break;
 1716     case SQRT:
 1717       result = sqrt(data + 1);
 1718       break;
 1719     }
 1720   return 1.0 + pref.node_radius_multiplier * pref.link_node_ratio * result;
 1721 }               /* get_link_size */
 1722 
 1723 
 1724 
 1725 /* Called for every event a link receives. Right now it's used to
 1726  * set a message in the statusbar and launch the popup */
 1727 static gboolean link_item_event(GooCanvasItem *item,
 1728                                 GooCanvasItem *target_item,
 1729                                 GdkEventButton *event,
 1730                                 canvas_link_t * canvas_link)
 1731 {
 1732   gchar *str;
 1733   const link_t *link=NULL;
 1734 
 1735   switch (event->type)
 1736   {
 1737     case GDK_BUTTON_PRESS:
 1738     case GDK_BUTTON_RELEASE:
 1739     case GDK_2BUTTON_PRESS:
 1740     case GDK_3BUTTON_PRESS:
 1741       if (canvas_link)
 1742         link_info_window_create(&canvas_link->canvas_link_id);
 1743       break;
 1744 
 1745     case GDK_ENTER_NOTIFY:
 1746       if (canvas_link)
 1747         link = links_catalog_find(&canvas_link->canvas_link_id);
 1748       if (link && link->main_prot[pref.stack_level])
 1749         str = g_strdup_printf(_("Link main protocol: %s"),
 1750                               link->main_prot[pref.stack_level]);
 1751       else
 1752         str = g_strdup_printf(_("Link main protocol: unknown"));
 1753       gtk_statusbar_push(appdata.statusbar, 1, str);
 1754       g_free(str);
 1755       break;
 1756     case GDK_LEAVE_NOTIFY:
 1757       gtk_statusbar_pop(appdata.statusbar, 1);
 1758       break;
 1759     default:
 1760       break;
 1761   }
 1762 
 1763   return FALSE;
 1764 }               /* link_item_event */
 1765 
 1766 
 1767 /* Called for every event a node receives. Right now it's used to
 1768  * launch the popup */
 1769 static gint node_item_event(GooCanvasItem *item,
 1770                             GooCanvasItem *target_item,
 1771                             GdkEventButton *event,
 1772                             canvas_node_t * canvas_node)
 1773 {
 1774 
 1775 //  gdouble item_x, item_y;
 1776   const node_t *node = NULL;
 1777 
 1778   /* This is not used yet, but it will be. */
 1779 /*  item_x = event->button.x;
 1780   item_y = event->button.y;
 1781   gnome_canvas_item_w2i (item->parent, &item_x, &item_y);
 1782 */
 1783   switch (event->type)
 1784     {
 1785 
 1786       case GDK_BUTTON_PRESS:
 1787       case GDK_BUTTON_RELEASE:
 1788       case GDK_2BUTTON_PRESS:
 1789       case GDK_3BUTTON_PRESS:
 1790         if (canvas_node)
 1791           node = nodes_catalog_find(&canvas_node->canvas_node_id);
 1792       if (node)
 1793         {
 1794           node_protocols_window_create(&canvas_node->canvas_node_id);
 1795           g_my_info ("Nodes: %d (shown %u)", nodes_catalog_size(),
 1796                      displayed_nodes);
 1797           if (DEBUG_ENABLED)
 1798             {
 1799               gchar *msg = node_dump(node);
 1800               g_my_debug("%s", msg);
 1801               g_free(msg);
 1802             }
 1803         }
 1804       break;
 1805     default:
 1806       break;
 1807     }
 1808 
 1809   return FALSE;
 1810 
 1811 }               /* node_item_event */
 1812 
 1813 /* Explain pcap stats in status bar when moused over */
 1814 static gint pcap_stats_text_item_event(GooCanvasItem *item, GdkEvent *event,
 1815                                        void *unused)
 1816 {
 1817   switch (event->type)
 1818     {
 1819     case GDK_ENTER_NOTIFY:
 1820       gtk_statusbar_push(appdata.statusbar, 1, _("'recv': packets received; "
 1821                                                  "'drop': packets dropped by OS buffering; "
 1822                                                  "'ifdrop': packets dropped by interface or driver."));
 1823       break;
 1824 
 1825     case GDK_LEAVE_NOTIFY:
 1826       gtk_statusbar_pop(appdata.statusbar, 1);
 1827       break;
 1828 
 1829     default:
 1830       break;
 1831     }
 1832 
 1833   return FALSE;
 1834 }
 1835 
 1836 /* Pushes a string into the statusbar stack */
 1837 void
 1838 set_statusbar_msg(gchar * str)
 1839 {
 1840   static gchar *status_string = NULL;
 1841 
 1842   if (status_string)
 1843     g_free (status_string);
 1844 
 1845   status_string = g_strdup (str);
 1846 
 1847   gtk_statusbar_pop(appdata.statusbar, 0);
 1848   gtk_statusbar_push(appdata.statusbar, 0, status_string);
 1849 }               /* set_statusbar_msg */
 1850 
 1851 
 1852 static gint canvas_node_compare(const node_id_t *a, const node_id_t *b,
 1853                                 gpointer dummy)
 1854 {
 1855   g_assert (a != NULL);
 1856   g_assert (b != NULL);
 1857   return node_id_compare(a, b);
 1858 }
 1859 
 1860 static gint canvas_link_compare(const link_id_t *a, const link_id_t *b,
 1861                                 gpointer dummy)
 1862 {
 1863   g_assert (a != NULL);
 1864   g_assert (b != NULL);
 1865   return link_id_compare(a, b);
 1866 }
 1867 
 1868 static void canvas_link_delete(canvas_link_t *canvas_link)
 1869 {
 1870    /* Right now I'm not very sure in which cases there could be a canvas_link but not a link_item, but
 1871    * I had a not in update_canvas_nodes that if the test is not done it can lead to corruption */
 1872   if (canvas_link->src_item)
 1873     {
 1874       goo_canvas_item_remove(canvas_link->src_item);
 1875       canvas_link->src_item = NULL;
 1876     }
 1877   if (canvas_link->dst_item)
 1878     {
 1879       goo_canvas_item_remove(canvas_link->dst_item);
 1880       canvas_link->dst_item = NULL;
 1881     }
 1882 
 1883   g_free (canvas_link);
 1884 }
 1885 
 1886 /* diagram timeout was changed. Remove old timer and register new */
 1887 void diagram_timeout_changed(void)
 1888 {
 1889   if (diagram_timeout)
 1890     g_source_remove(diagram_timeout);
 1891   diagram_timeout = g_timeout_add(pref.refresh_period,
 1892                                             update_diagram_callback,
 1893                                             NULL);
 1894 }
 1895 
 1896 void resize_diagram(const GtkAllocation *allocation)
 1897 {
 1898   goo_canvas_set_bounds(gcanvas_,
 1899                         -allocation->width / 2,
 1900                         -allocation->height / 2,
 1901                         allocation->width / 2,
 1902                         allocation->height / 2);
 1903   ask_reposition(FALSE);
 1904   redraw_canvas_background(gcanvas_);
 1905   update_diagram(gcanvas_);
 1906 }
 1907 
 1908 gboolean update_diagram_callback(gpointer data)
 1909 {
 1910    update_diagram(gcanvas_);
 1911    return TRUE;
 1912 }